Mengembalikan halaman memori ke OS

Mengosongkan objek dalam kode aplikasi atau menutup aliran data sementara tidak selalu mengembalikan RAM fisik ke sistem operasi. Alokator native kustom, runtime pembersihan sampah memori, dan tabel hash alamat terbuka sering kali mempertahankan arena memori virtual yang tetap didukung oleh halaman fisik lama setelah data logis di dalamnya hilang. Demikian pula, membaca atau memetakan memori arsip aset besar akan mengisi cache halaman kernel, yang tetap dibebankan ke grup kontrol memori (memcg) aplikasi Anda.

Halaman ini menjelaskan cara menggunakan panggilan sistem madvise dan posix_fadvise untuk mengembalikan halaman anonim dan yang didukung file yang tidak digunakan ke OS, sekaligus menjaga reservasi alamat virtual dan deskriptor file tetap utuh, serta cara memverifikasi dampaknya di adb shell dan Perfetto.

Alasan dealokasi logis mempertahankan RAM fisik

Kernel Linux melacak memori fisik pada tingkat perincian halaman hardware, yang berukuran 4 KB atau 16 KB di perangkat Android. Saat proses mencadangkan region ruang alamat virtual dengan mmap, kernel membuat area memori virtual (VMA) tanpa mengalokasikan RAM fisik. Kernel mengalokasikan dan mengisi halaman fisik hanya saat proses pertama kali menyentuh halaman tersebut. Untuk memori anonim, pembacaan pertama memetakan halaman nol bersama, sehingga hanya operasi tulis yang mengalokasikan RAM.

Setelah halaman mengalami fault ke RAM, kernel tidak mengetahui apakah byte di dalam halaman tersebut masih mewakili objek aktif dalam runtime aplikasi Anda. Dua pola umum membuat halaman fisik tetap berada di memori setelah aplikasi Anda selesai menggunakannya:

  • Arena dan kumpulan anonim: Alokator native kustom (seperti alokator slab, kumpulan, atau arena frame), runtime terkelola dengan ruang heap yang tidak bergerak, dan tabel hash besar mencadangkan rentang alamat virtual yang berdekatan dengan mmap menggunakan MAP_PRIVATE | MAP_ANONYMOUS. Saat aplikasi Anda mereset arena, mengosongkan blok kembali ke kumpulan kustom, atau menghapus tabel hash dengan mengosongkan metadatanya, halaman yang mendasarinya tetap kotor. Tanpa petunjuk eksplisit ke kernel, halaman tersebut akan terus dihitung dalam resident set size (RSS) dan proportional set size (PSS) anonim proses Anda dan biaya cgroup memory.current-nya. Di bawah tekanan memori, kernel mengompresinya ke dalam ZRAM, tempat halaman tersebut masih menempati ruang swap yang dibebankan ke memory.swap.current.
  • Cache halaman yang didukung file dari streaming aset: Saat aplikasi Anda membaca paket aset besar, arsip level, penampung media, atau model machine learning menggunakan read, pread, atau mmap yang didukung file, kernel akan meng-cache setiap halaman yang dibaca di cache halaman dan mengenakan biaya halaman tersebut ke penghitung file di file memory.stat cgroup proses Anda. Jika aplikasi Anda mendekompresi atau menyalin aset tersebut ke dalam buffer runtime anonim atau menguploadnya ke GPU, menyimpan halaman file mentah di cache halaman akan menduplikasi aset di RAM.
Halaman arena kotor yang dirilis dengan madvise dan halaman aset yang di-cache
       dihapus dengan posix_fadvise, sementara rentang virtual dan deskriptor file
       tetap utuh.
Gambar 1. Mengklaim kembali halaman fisik dari arena virtual dan cache halaman.

Merilis halaman anonim

Panggilan sistem madvise memberikan saran kepada kernel tentang cara proses Anda menggunakan rentang alamat virtual. Untuk pemetaan anonim pribadi (MAP_PRIVATE | MAP_ANONYMOUS), meneruskan MADV_DONTNEED akan menginstruksikan kernel untuk segera menghapus halaman fisik dan entri ZRAM yang di-swap yang mendukung rentang yang selaras dengan halaman yang ditentukan.

Setelah madvise dengan MADV_DONTNEED menampilkan:

  • Rentang alamat virtual tetap dicadangkan dan valid di ruang alamat proses Anda (VmSize tidak berubah), sehingga pointer dan batas arena tetap utuh tanpa memanggil munmap atau mmap.
  • Halaman fisik akan segera dibatalkan pemetaannya dan dikosongkan, sehingga mengurangi RssAnon, Pss, penghitung anon di memory.stat, dan memory.swap.current untuk halaman apa pun yang ada di ZRAM.
  • Setiap pembacaan atau penulisan berikutnya ke alamat dalam rentang tersebut akan mengalami kesalahan di halaman baru yang diisi nol sesuai permintaan.
Tanda saran Pemetaan target Perilaku kernel di Android
MADV_DONTNEED Anonim pribadi (MAP_PRIVATE | MAP_ANONYMOUS) Segera menghapus halaman fisik dan slot swap ZRAM. Akses berikutnya mengalami error di halaman yang diisi nol. Penurunan deterministik dalam RssAnon dan memory.current.
MADV_DONTNEED Didukung file (MAP_PRIVATE atau MAP_SHARED) Membatalkan pemetaan halaman dari tabel halaman proses (RssFile penurunan). Halaman copy-on-write pribadi dalam pemetaan MAP_PRIVATE akan dihapus, sehingga modifikasi akan hilang dan akses berikutnya akan membaca ulang file. Halaman MAP_SHARED yang kotor akan ditulis kembali ke penyimpanan nanti. Halaman bersih tetap berada di cache halaman hingga dihapus.
MADV_FREE Anonim pribadi (MAP_PRIVATE | MAP_ANONYMOUS) Menandai halaman sebagai dapat digunakan kembali secara lambat. Kernel mempertahankan halaman di RssAnon jika tidak ada tekanan memori dan hanya mengklaimnya kembali saat klaim kembali global atau memcg berjalan.
MADV_WILLNEED Anonim yang didukung file atau di-swap Memulai readahead asinkron untuk pemetaan yang didukung file, atau membaca halaman anonim yang di-swap kembali dari ZRAM ke cache swap lebih awal.

Menyelaraskan rentang interior dengan ukuran halaman runtime

madvise beroperasi di seluruh halaman. Fungsi ini memerlukan alamat awal yang selaras dengan halaman dan membulatkan panjangnya ke batas halaman berikutnya. Jika Anda merilis span di dalam arena bersama tempat byte di sekitarnya masih menyimpan objek aktif, pembulatan rentang ke luar dapat mengosongkan alokasi aktif yang berbagi halaman batas.

Selalu buat kueri ukuran halaman runtime dengan sysconf(_SC_PAGESIZE) untuk mendukung perangkat halaman 4 KB dan 16 KB, serta bulatkan alamat awal interior ke batas halaman berikutnya dan alamat akhir interior ke batas halaman sebelumnya. Untuk panduan selengkapnya tentang pengalokasi yang tidak bergantung pada ukuran halaman, lihat Mengoptimalkan pengalokasi memori kustom.

#include <sys/mman.h>
#include <unistd.h>
#include <cstdint>

// Reclaims physical pages strictly inside [region_start, region_start + length)
// without disturbing live objects on partially occupied boundary pages.
void ReleaseUnusedArenaSpan(void* region_start, size_t length) {
  const uintptr_t page_size = static_cast<uintptr_t>(sysconf(_SC_PAGESIZE));
  const uintptr_t page_mask = ~(page_size - 1);

  const uintptr_t raw_start = reinterpret_cast<uintptr_t>(region_start);
  const uintptr_t raw_end = raw_start + length;

  // Round start UP and end DOWN so only fully unused pages are discarded.
  const uintptr_t aligned_start = (raw_start + page_size - 1) & page_mask;
  const uintptr_t aligned_end = raw_end & page_mask;

  if (aligned_end > aligned_start) {
    madvise(reinterpret_cast<void*>(aligned_start),
            aligned_end - aligned_start,
            MADV_DONTNEED);
  }
}

Saat membuat pemetaan anonim kustom dengan mmap, beri label VMA dengan prctl menggunakan PR_SET_VMA_ANON_NAME sehingga Anda dapat melacak residensinya berdasarkan nama di showmap dan /proc/<pid>/smaps. dumpsys meminfo menghitung region anonim yang diberi nama kustom di bagian Unknown. Beberapa kernel lama menyimpan pointer ke nama, bukan menyalinnya, jadi teruskan string yang tetap valid selama masa aktif pemetaan, seperti literal string:

#include <sys/mman.h>
#include <sys/prctl.h>

#ifndef PR_SET_VMA
#define PR_SET_VMA 0x53564d41
#define PR_SET_VMA_ANON_NAME 0
#endif

void* ReserveNamedArena(size_t capacity_bytes, const char* arena_name) {
  void* ptr = mmap(nullptr, capacity_bytes, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (ptr != MAP_FAILED) {
    prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME,
          reinterpret_cast<unsigned long>(ptr), capacity_bytes,
          reinterpret_cast<unsigned long>(arena_name));
  }
  return ptr;
}

Mengeluarkan halaman cache file

Saat aplikasi Anda membaca data dari deskriptor file, kernel akan meng-cache halaman file di cache halaman untuk mengantisipasi pembacaan di masa mendatang. Meskipun mempercepat akses berulang ke file konfigurasi kecil atau pustaka bersama, hal ini akan merugikan Anda saat melakukan streaming arsip aset besar, paket tekstur, atau potongan audio dan video yang diuraikan sekali dan disimpan dalam bentuk yang didekode di tempat lain.

Panggilan sistem posix_fadvise memungkinkan aplikasi Anda memberi tahu kernel tentang pola akses file yang diharapkan pada deskriptor file yang terbuka:

  • POSIX_FADV_DONTNEED: Menginstruksikan kernel bahwa rentang byte yang ditentukan tidak akan diakses dalam waktu dekat. Kernel segera mengeluarkan halaman yang di-cache yang bersih dan tidak dipetakan dalam rentang yang selaras dengan halaman tersebut dari cache halaman, sehingga mengurangi penghitung file di memory.stat dan biaya memory.current cgroup Anda.
  • POSIX_FADV_SEQUENTIAL: Menggandakan periode readahead kernel untuk deskriptor file, sehingga meningkatkan throughput saat melakukan streaming arsip besar dari awal hingga akhir. Gabungkan ini dengan POSIX_FADV_DONTNEED pada potongan berikutnya saat Anda menggunakannya sehingga jejak cache halaman tetap terikat pada jendela geser kecil.
  • POSIX_FADV_RANDOM: Menonaktifkan readahead kernel spekulatif pada deskriptor file. Gunakan ini saat melakukan pencarian sparse yang tidak berdekatan berdasarkan offset byte ke dalam file arsip atau database yang besar sehingga setiap pembacaan kecil tidak menarik halaman yang tidak digunakan yang berdekatan ke dalam cache halaman. Untuk file yang dipetakan memori, panggil madvise dengan MADV_RANDOM pada pemetaan.

Contoh berikut men-streaming arsip aset besar dalam potongan, sehingga memungkinkan readahead berurutan di awal dan mengeluarkan setiap span yang selaras dengan halaman yang diproses dari cache halaman:

#include <fcntl.h>
#include <unistd.h>
#include <cstdint>
#include <vector>

void StreamAssetArchive(int fd, size_t total_size) {
  const size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
  constexpr size_t kChunkSize = 4 * 1024 * 1024;  // 4 MB streaming window
  std::vector<uint8_t> buffer(kChunkSize);

  // Hint sequential access so the kernel prefetches upcoming chunks.
  posix_fadvise(fd, 0, total_size, POSIX_FADV_SEQUENTIAL);

  off_t offset = 0;
  while (offset < static_cast<off_t>(total_size)) {
    ssize_t bytes_read = pread(fd, buffer.data(), buffer.size(), offset);
    if (bytes_read <= 0) break;

    // Decode or upload buffer[0 .. bytes_read) into runtime structures here.

    // Evict full pages that have been completely consumed.
    const size_t aligned_len =
        (static_cast<size_t>(bytes_read) / page_size) * page_size;
    if (aligned_len > 0) {
      posix_fadvise(fd, offset, static_cast<off_t>(aligned_len),
                    POSIX_FADV_DONTNEED);
    }
    offset += bytes_read;
  }
}

Mendapatkan kembali region mmap yang didukung file

Jika aplikasi Anda memetakan file aset besar ke memori dengan mmap, bukan membacanya dengan pread, memanggil posix_fadvise dengan POSIX_FADV_DONTNEED saja tidak akan mengeluarkan halaman yang masih dipetakan ke dalam tabel halaman proses Anda. Untuk menghapus rentang pemetaan file hanya baca yang digunakan tanpa membatalkan pemetaan seluruh VMA:

  1. Panggil madvise(addr, length, MADV_DONTNEED) pada rentang alamat virtual yang dipetakan untuk membatalkan pemetaan halaman tersebut dari tabel halaman proses Anda (RssFile dihapus).
  2. Panggil posix_fadvise(fd, offset, length, POSIX_FADV_DONTNEED) pada offset file yang sesuai sehingga kernel juga mengeluarkan halaman bersih yang tidak dipetakan dari cache halaman (file dalam penurunan memory.stat).

Mengikuti panduan saran halaman

Terapkan praktik ini saat mengintegrasikan madvise dan posix_fadvise ke dalam alokator, pemuat aset, dan pengendali siklus proses:

  • Mengelompokkan panggilan ke dalam region kasar: Setiap panggilan madvise mengambil kunci peta memori proses Anda (mmap_lock), menelusuri tabel halaman, dan membatalkan validasi entri buffering lookaside terjemahan (TLB) yang cocok di core CPU lain (dengan petunjuk pemeliharaan TLB siaran di Arm dan interupsi antar-prosesor di x86). Jangan panggil madvise pada setiap dealokasi kecil. Gabungkan span kosong dan beri tahu kernel dalam potongan berukuran 64 KB hingga 1 MB atau lebih besar, atau sapu span kosong selama batas frame dan transisi latar belakang (onStop dan TRIM_MEMORY_UI_HIDDEN).
  • Klaim kembali kapasitas kosong dalam tabel hash dan kumpulan: Saat tabel hash, kumpulan objek, atau buffering staging yang besar dihapus setelah transisi scene atau sinkronisasi data, perkecil alokasi pendukungnya atau keluarkan MADV_DONTNEED di seluruh array pendukung yang dihapus. Mempertahankan tabel beralamat terbuka 32 MB yang dialokasikan dengan tombstone akan membuat semua halaman fisik 32 MB dibebankan ke proses Anda.
  • Menghapus data file kotor sebelum mengeluarkannya: POSIX_FADV_DONTNEED hanya mengeluarkan halaman cache halaman yang bersih. Jika aplikasi Anda menulis file sementara atau cache berukuran besar yang tidak akan segera dibaca kembali, panggil fdatasync sebelum posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) agar halaman kotor ditulis ke penyimpanan, lalu dihapus dari RAM.
  • Jangan mengeluarkan kode atau aset bersama yang sering digunakan: Hindari memanggil POSIX_FADV_DONTNEED pada DEX, ODEX, VDEX, pustaka bersama (.so), atau aset UI yang diakses berulang kali. Mengeluarkan halaman kode atau halaman aset yang aktif akan memaksa refault penyimpanan sinkron yang menghentikan thread dan meningkatkan tekanan I/O, seperti yang dilaporkan oleh informasi penghentian tekanan (PSI).

Memverifikasi reklamasi halaman di shell

Pastikan madvise dan posix_fadvise mengklaim kembali RAM fisik menggunakan showmap dan file akuntansi cgroup v2 aplikasi Anda.

Memeriksa ukuran virtual versus fisik dengan showmap

Saat Anda memberi nama arena anonim dengan PR_SET_VMA_ANON_NAME, showmap akan menampilkan pemesanan virtualnya (virtual size) dan halaman fisik residennya (RSS dan PSS). Contoh output berikut berasal dari aplikasi contoh MemoryLab di AOSP, yang memberi nama arenanya memorylab-arena. Ganti com.example.app dan memorylab-arena dengan nama paket dan nama arena Anda. Sebelum memanggil madvise dengan MADV_DONTNEED di arena 64 MB yang disentuh, jalankan:

adb shell "showmap \$(pidof com.example.app) | grep -E '^ +virtual|^ +size|memorylab-arena' | head -3"
 virtual                     shared   shared  private  private                   Anon      Shmem     File      Shared   Private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS HugePages PmdMapped PmdMapped Hugetlb  Hugetlb    Locked    # object
   65536    65536    65536        0        0        0    65536        0        0         0         0         0        0        0        0    1 [anon:memorylab-arena]

Setelah memanggil madvise(arena_ptr, 64 * 1024 * 1024, MADV_DONTNEED), jalankan showmap lagi:

 virtual                     shared   shared  private  private                   Anon      Shmem     File      Shared   Private
    size      RSS      PSS    clean    dirty    clean    dirty     swap  swapPSS HugePages PmdMapped PmdMapped Hugetlb  Hugetlb    Locked    # object
   65536        0        0        0        0        0        0        0        0         0         0         0        0        0        0    1 [anon:memorylab-arena]

virtual size tetap di 65536 KB (64 MB), yang mengonfirmasi bahwa rentang alamat virtual masih dicadangkan, sedangkan RSS, PSS, dan private dirty turun ke 0 KB.

Memeriksa memory.current dan memory.stat cgroup

Untuk memverifikasi reklamasi halaman anonim dan penghapusan cache halaman yang tidak dipetakan (yang tidak muncul dalam tabel halaman proses jika dibaca dengan pread), periksa direktori cgroup v2 proses. Di Android, setiap proses aplikasi memiliki cgroup-nya sendiri (seperti /sys/fs/cgroup/apps/uid_<UID>/pid_<PID>/, bergantung pada perangkat). Perintah berikut menemukan direktori dari baris 0:: dari /proc/<PID>/cgroup dan mencetak penghitung:

adb shell '
  PID=$(pidof com.example.app)
  CG=/sys/fs/cgroup$(sed -n "s/^0:://p" /proc/$PID/cgroup)
  echo "memory.current: $(cat $CG/memory.current)"
  echo "memory.swap.current: $(cat $CG/memory.swap.current)"
  grep -E "^(anon|file) " $CG/memory.stat
'

Sebelum merilis arena anonim 64 MB dan mengeluarkan file aset yang dipetakan memori 128 MB dari cache halaman:

memory.current: 242962432
memory.swap.current: 16633856
anon 100671488
file 140009472

Segera setelah memanggil madvise dengan MADV_DONTNEED di arena dan di pemetaan file aset, lalu posix_fadvise dengan POSIX_FADV_DONTNEED di deskriptor file aset:

memory.current: 41766912
memory.swap.current: 16502784
anon 33722368
file 5914624

anon turun sebesar 64 MB dan file turun sebesar 128 MB, sehingga mengurangi memory.current sekitar 192 MB tanpa menutup deskriptor file atau membatalkan pemetaan ruang alamat virtual arena. memory.swap.current hampir tidak berubah, karena hampir tidak ada halaman arena yang telah ditukar ke ZRAM. Nilai Anda bergantung pada perangkat dan seberapa banyak file yang telah dikeluarkan kernel dengan sendirinya. Untuk mengetahui informasi selengkapnya tentang cara kernel mengisi dan mengklaim kembali memori per cgroup, lihat Klaim kembali memori: penghapusan dan pertukaran.

Menganalisis reklamasi halaman di Perfetto

Perfetto memungkinkan Anda menghubungkan panggilan madvise dan posix_fadvise dengan penghitung tabel halaman dan cache halaman kernel pada linimasa terpadu.

Merekam aktivitas

Ambil rekaman aktivitas dengan linux.process_stats untuk penghitung proses berkala dan linux.ftrace untuk peristiwa RSS dan cache halaman real-time, lalu tarik ke komputer Anda:

adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/page_advice.perfetto-trace <<EOF
buffers: {
    size_kb: 65536
    fill_policy: RING_BUFFER
}
data_sources: {
    config {
        name: "linux.process_stats"
        process_stats_config {
            scan_all_processes_on_start: true
            proc_stats_poll_ms: 100
        }
    }
}
data_sources: {
    config {
        name: "linux.ftrace"
        ftrace_config {
            ftrace_events: "kmem/rss_stat"
            ftrace_events: "filemap/mm_filemap_add_to_page_cache"
            ftrace_events: "filemap/mm_filemap_delete_from_page_cache"
        }
    }
}
duration_ms: 20000
EOF
adb pull /data/misc/perfetto-traces/page_advice.perfetto-trace

Memeriksa jalur di UI Perfetto

Buka rekaman aktivitas di UI Perfetto, luaskan grup proses aplikasi Anda, dan sematkan jalur penghitung berikut agar tetap berada di bagian atas linimasa:

  • mem.virt: Tetap datar di seluruh panggilan MADV_DONTNEED, yang mengonfirmasi bahwa reservasi alamat virtual (mmap) tidak berubah.
  • mem.rss.anon: Turun tajam pada stempel waktu yang tepat saat aplikasi Anda memanggil madvise dengan MADV_DONTNEED di arena anonim yang kotor atau region heap.
  • mem.rss.file: Turun saat aplikasi Anda memanggil madvise dengan MADV_DONTNEED di region mmap yang didukung file, disertai dengan burst peristiwa ftrace mm_filemap_delete_from_page_cache saat posix_fadvise dengan POSIX_FADV_DONTNEED mengeluarkan halaman bersih dari cache halaman.
  • mem.swap: Turun saat MADV_DONTNEED menghapus halaman anonim yang sebelumnya telah dikompresi ke ZRAM.
Pelacakan penghitung Perfetto untuk satu proses. mem.rss.anon dan
       mem.rss.file meningkat saat arena dan file aset diisi dan
       menurun setelah madvise dan posix_fadvise, sedangkan mem.swap dan
       mem.virt tetap datar.
Gambar 2. RSS menurun setelah panggilan saran halaman.

Gambar 2 menunjukkan jalur ini untuk aplikasi contoh MemoryLab. Untuk menjelajahi tampilan yang sama, buka rekaman aktivitas contoh di UI Perfetto.

Membuat kueri memori puncak dan akhir dengan PerfettoSQL

Jalankan kueri PerfettoSQL berikut di UI Perfetto (tab Query (SQL)) untuk membandingkan nilai puncak dan akhir mem.virt, mem.rss.anon, mem.rss.file, dan mem.swap untuk proses target Anda:

WITH counters AS (
  SELECT t.name AS counter_name, c.ts, c.value
  FROM counter c
  JOIN process_counter_track t ON c.track_id = t.id
  JOIN process p USING (upid)
  WHERE p.name = 'com.example.app'
    AND t.name IN ('mem.virt', 'mem.rss.anon', 'mem.rss.file', 'mem.swap')
)
SELECT
  counter_name,
  CAST(MAX(value) / 1048576 AS INT) AS peak_mb,
  CAST((
    SELECT value FROM counters AS last
    WHERE last.counter_name = counters.counter_name
    ORDER BY ts DESC LIMIT 1
  ) / 1048576 AS INT) AS final_mb
FROM counters
GROUP BY counter_name
ORDER BY counter_name;

Contoh output untuk rekaman aktivitas pada Gambar 2:

counter_name  peak_mb  final_mb
mem.rss.anon      111        48
mem.rss.file      212        84
mem.swap           36        36
mem.virt        13952     13952

mem.rss.anon turun sebesar 63 MB dan mem.rss.file turun sebesar 128 MB dari puncaknya, sedangkan mem.virt berakhir di puncaknya karena pemesanan virtual tidak berubah.

Menyelesaikan halaman yang dikeluarkan ke file

Peristiwa mm_filemap_add_to_page_cache dan mm_filemap_delete_from_page_cache mengidentifikasi file berdasarkan nomor inode (i_ino), bukan berdasarkan jalur. Untuk mengonfirmasi bahwa halaman yang dihapus adalah milik file yang Anda sarankan, hitung penghapusan per inode:

SELECT
  EXTRACT_ARG(arg_set_id, 'i_ino') AS inode,
  COUNT(*) AS evicted_pages
FROM ftrace_event
WHERE name = 'mm_filemap_delete_from_page_cache'
GROUP BY inode
ORDER BY evicted_pages DESC
LIMIT 5;

Contoh output untuk rekaman aktivitas pada Gambar 2:

inode     evicted_pages
9761              35781
76792192            767
126                 668
0                   612
11587712            514

Saat perangkat yang merekam rekaman aktivitas masih terhubung, selesaikan setiap inode ke jalur. Nomor inode hanya unik dalam satu sistem file, jadi batasi penelusuran ke sistem file yang menyimpan data aplikasi Anda:

INODE=<inode from the query>
adb shell "find /data -xdev -inum $INODE 2>/dev/null"

Contoh output untuk inode 9761:

/data/user/0/com.android.memorylab/files/asset_pack.bin
/data/data/com.android.memorylab/files/asset_pack.bin

Kedua jalur merujuk ke file yang sama, karena /data/user/0 dan /data/data adalah dua nama untuk direktori data aplikasi yang sama. Jumlah penghapusan untuk file aset (35.781 halaman) lebih besar daripada file itu sendiri (32.768 halaman pada 4 KB), karena kswapd telah menghapus sebagian file di bawah tekanan memori sebelum aplikasi membacanya kembali dan memanggil posix_fadvise.

Untuk skrip yang menyelesaikan banyak inode sekaligus, lihat Menyelesaikan kesalahan pada inode, lalu pada file.


← Interaksi kswapd dan lmkd | ↑ Atas | Proses latar belakang yang ramping →