Untuk memahami penggunaan memori di Android, Anda harus melihat sistem dari beberapa perspektif yang berbeda, mulai dari objek Java tingkat tinggi hingga halaman kernel tingkat rendah.
Penurunan performa
Penggunaan memori itu sendiri bukanlah hal yang baik atau buruk; yang penting adalah untuk apa Anda menggunakan memori tersebut. Namun, saat Anda mendekati batas memori yang tersedia di perangkat, Anda pada akhirnya akan "mengalami penurunan performa".

Saat Anda berada jauh dari tebing, menambahkan sedikit penggunaan memori (misalnya, 50 MB) mungkin tidak akan berdampak nyata pada performa. Namun, batas akan tercapai saat RAM yang tersedia habis. Pada tahap ini, sistem operasi harus mulai melakukan paging out memori dan menghentikan proses untuk mengosongkan ruang. Di luar batas ini, peningkatan kecil dalam penggunaan memori dapat menyebabkan "thrashing" yang signifikan, di mana perangkat menjadi tidak responsif atau tampak dimulai ulang karena proses penting dihentikan.
Stack Android
Setiap lapisan stack Android memiliki tampilan memori uniknya sendiri:

- Aplikasi (Java/Kotlin): Developer terutama melihat objek Java yang dialokasikan di heap Java.
- Android Runtime (ART): ART mengelola heap Java/Kotlin menggunakan halaman memori virtual dari kernel. Sebelumnya dikenal sebagai Dalvik (istilah ini terkadang digunakan secara bergantian).
- Kernel Linux: Kernel melihat memori dalam bentuk halaman fisik dan virtual. Biasanya, ukuran ini adalah 4 KB, tetapi Android juga mendukung ukuran halaman 16 KB. Kernel tidak mengetahui apa pun tentang "objek Java".
Jika ingin mengoptimalkan memori, Anda harus mengoptimalkan dalam lapisan Anda sendiri (misalnya, memuat lebih sedikit bitmap) atau memahami lapisan di bawah Anda untuk melihat bagaimana alokasi tingkat tinggi Anda diterjemahkan ke penggunaan halaman fisik.
Jenis memori: anonim vs. didukung file
Sebelum mempelajari cara OS mengklaim kembali memori, Anda harus memahami dua kategori dasar halaman memori di Linux:
- Memori Anonim (
anon): Memori yang tidak didukung oleh file di penyimpanan. Ini mencakup alokasi memori olehmallocC/C++ (seperti alokator Scudo) dan alokasi memori untuk objek Java di heap Java/Kotlin. Karena memori ini tidak memiliki file sumber untuk dikembalikan, OS harus menyimpannya di RAM atau mengompresi dan menukarnya ke ZRAM saat berada di bawah tekanan. - Memori yang Didukung File (
file): Memori yang dipetakan langsung dari file di penyimpanan. Hal ini mencakup kode yang dapat dieksekusi (file DEX, library native.so), dan font.
File yang dipetakan memori (mmap)
Android menggunakan mmap untuk memetakan memori yang didukung file dan anonim ke dalam
ruang alamat proses.
Saat menangani memori yang didukung file, halaman diklasifikasikan lebih lanjut menjadi:
- Memori Bersih: Halaman yang dipetakan dari file yang belum diubah. Jika sistem memerlukan lebih banyak memori, kernel dapat menghapus halaman ini, karena halaman tersebut dapat dimuat ulang langsung dari file di penyimpanan nanti.
- Memori Kotor: Halaman yang telah diubah oleh proses. Halaman ini tidak dapat dihapus; halaman ini berperilaku seperti memori anonim dan harus disimpan di RAM atau dipindahkan ke ZRAM.
Android lebih memilih format file (seperti DEX) yang dapat dipetakan ke memori, sehingga sistem dapat dengan mudah mengklaim kembali memori bersih saat berada dalam tekanan. Karena memori anonim dan kotor tidak dapat dihapus begitu saja, memori kotor/anonim pribadi yang tinggi adalah penyebab utama thrashing sistem dan intervensi Low Memory Killer (LMK). Jika aplikasi Anda menggunakan banyak memori kotor pribadi, Anda berkontribusi langsung pada penurunan performa.
Model proses Zygote
Android meminimalkan biaya memulai aplikasi baru dengan menggunakan proses yang disebut Zygote.
- Zygote dimulai saat booting dan melakukan pramuat class dan resource framework umum ke dalam memorinya. Dari perspektif kernel, ini menjadi memori kotor anonim, tetapi unik untuk proses Zygote.
- Saat aplikasi baru dimulai, sistem akan mengambil proses Zygote.
- Proses turunan baru mewarisi memori Zygote menggunakan pemetaan bersama dengan semantik copy-on-write (COW).

Selama proses turunan hanya membaca memori yang diwarisi dari Zygote, halaman memori fisik akan tetap dibagikan di antara semua proses. Saat proses turunan mengubah halaman bersama, kernel secara transparan membuat salinan pribadi halaman tersebut untuk proses tersebut. Model ini memungkinkan banyak proses berbagi sebagian besar memorinya—terutama kode dan resource framework—sehingga secara signifikan mengurangi jejak memori sistem secara keseluruhan.
RSS, PSS, USS, dan memcg
Karena memori sangat banyak digunakan bersama di antara proses—terutama melalui model Zygote—ada beberapa cara untuk memperhitungkan penggunaan memori proses. RSS, PSS, dan USS menghitung halaman yang dipetakan ke dalam ruang alamat proses:

- RSS (Resident Set Size): Jumlah total halaman yang dimiliki proses dalam RAM. Hal ini melebih-lebihkan penggunaan karena menghitung halaman bersama beberapa kali (sekali untuk setiap proses yang membagikannya).
- PSS (Proportional Set Size): Jumlah total memori unik untuk proses, ditambah bagian proporsional dari memori bersama. Jika halaman dibagikan oleh 5 proses, setiap proses akan dikenai biaya sebesar 1/5 dari halaman tersebut.
- USS (Unique Set Size): Jumlah memori yang unik untuk proses. Ini adalah memori yang akan dikembalikan ke sistem jika proses dihentikan.
memcg: akuntansi kernel itu sendiri
Kernel juga menyimpan total memori yang berjalan dan digunakan oleh setiap proses, baik memori tersebut dipetakan maupun tidak. Di Android, setiap proses aplikasi
termasuk dalam grup kontrol memori (memcg)-nya sendiri, yang disusun bertingkat di dalam
memcg untuk seluruh aplikasi (lihat Hierarki memcg Android).
Setiap kali halaman memori dialokasikan, kernel membebankannya ke memcg
proses yang mengalokasikannya. Total biaya ada dalam file memory.current
memcg.
Akuntansi memcg berbeda dengan RSS, PSS, dan USS dalam beberapa hal penting:
- Setiap halaman dikenai biaya tepat satu kali, ke memcg proses yang pertama kali membawanya ke memori. Biaya tetap ada meskipun proses lain memetakan halaman yang sama nanti. Halaman yang dipramuat Zygote akan ditagihkan ke Zygote, bukan ke aplikasi Anda. Saat aplikasi Anda menulis ke salah satu halaman ini, salinan pribadi akan ditagihkan ke aplikasi Anda.
- Metrik ini mencakup memori yang tidak dapat dilihat oleh metrik tabel halaman.
memory.currentmenghitung halaman file yang dibaca aplikasi Anda tanpa memetakannya (misalnya, halaman database yang dibaca denganreadataupread). - Alat ini melacak swap secara terpisah. Memori yang dikompresi ke ZRAM akan dipindahkan dari
memory.currentkememory.swap.current, sehingga Anda dapat melihat keduanya tanpa menebak-nebak. - Biaya pembacaannya murah. Penghitung selalu diperbarui, dan membacanya tidak akan mengunci atau memindai ruang alamat aplikasi Anda.
- Ini adalah batas sistem. Batas memori dan anggaran memori yang diterapkan Android ke aplikasi bertindak berdasarkan biaya memcg.
Memori grafis, seperti buffer di balik bitmap hardware, umumnya tidak dibebankan ke memcg aplikasi Anda.
Metrik mana yang harus digunakan, dan kapan?
Memilih metrik memori yang tepat bergantung pada apa yang ingin Anda ukur dan lingkungan tempat Anda mengukurnya. Di perangkat yang menjalankan Android 17 QPR2 (API level 37.2) dan yang lebih tinggi, mulailah dengan memcg saat Anda melacak aplikasi Anda sendiri.
| Metrik | Paling Sesuai Untuk | Kekuatan Utama | Kekurangan Utama |
|---|---|---|---|
| memcg | Melacak setiap aplikasi dari waktu ke waktu, membandingkannya dengan anggaran | Menghitung setiap halaman satu kali, menyertakan cache halaman yang tidak dipetakan, melacak swap secara terpisah. Murah untuk dibaca. | Tidak tersedia di semua perangkat sebelum Android 17 QPR2. Halaman bersama dikenakan biaya untuk proses yang menyentuhnya terlebih dahulu. |
| PSS | Snapshot memori di seluruh sistem | Membagi memori bersama secara akurat. Menjumlahkan PSS sama dengan total memori yang digunakan. | Berfluktuasi berdasarkan proses lain yang sedang berjalan. Tidak cocok untuk membandingkan aplikasi tertentu dari waktu ke waktu. |
| RSS | Melacak aplikasi individual dari waktu ke waktu, telemetri lapangan | Stabil, tidak bergantung pada proses lain. Cepat dan murah untuk dikumpulkan. | Melebih-lebihkan total penggunaan karena menghitung memori bersama beberapa kali. |
| USS | Menilai dampak penghentian proses | Menunjukkan dengan tepat berapa banyak memori yang akan dikosongkan jika aplikasi dihentikan. | Mengabaikan memori bersama sepenuhnya. |
memcg (biaya grup kontrol memori)
- Paling cocok untuk: Melacak penggunaan memori setiap aplikasi dari waktu ke waktu, di seluruh Perjalanan Penting Pengguna (CUJ), atau di antara versi aplikasi, di perangkat yang menjalankan Android 17 QPR2 atau yang lebih baru. Metrik ini juga perlu diperhatikan saat Anda mengalibrasi anggaran memori, karena merupakan angka yang dibatasi oleh anggaran.
- Alasan: Seperti RSS, pengisian daya memcg tidak berubah saat aplikasi lain mulai atau berhenti berbagi halaman dengan aplikasi Anda. Tidak seperti RSS, pengisian daya memcg tidak pernah menghitung halaman dua kali, sehingga Anda dapat menjumlahkan pengisian daya semua proses yang berjalan di aplikasi Anda. Memcg seluruh aplikasi dapat lebih tinggi dari jumlah tersebut, karena menyimpan halaman file yang di-cache dari proses yang keluar hingga kernel mengklaimnya kembali. Metrik ini juga menghitung memori yang tidak dihitung oleh RSS, PSS, dan USS, seperti cache file yang tidak dipetakan. Membacanya murah, karena kernel memperbarui penghitung saat mengisi halaman.
- Cara membacanya: Baca
memory.current,memory.swap.current, danmemory.statdi direktori memcg suatu proses (lihat statistik memcg). Di Android 18 dan yang lebih tinggi,dumpsys meminfojuga mencetaknya. Dari dalam aplikasi Anda, gunakanMemoryBudgetManager. - Praktik Terbaik Pengukuran Lapangan: Untuk alasan yang sama seperti yang dijelaskan dalam
RSS, lacak memori anonim + swap: kolom
anondarimemory.statditambahmemory.swap.current. Kolomfiledarimemory.statmenyertakan halaman file yang dibaca aplikasi Anda sekali dan belum diklaim kembali oleh kernel, sehinggamemory.currentdapat tetap tinggi untuk sementara waktu setelah aplikasi Anda berhenti menggunakan memori tersebut. - Kapan harus berhati-hati: Karena halaman dibebankan ke proses yang menyentuhnya terlebih dahulu, aplikasi Anda akan dikenai biaya untuk halaman file bersama (seperti font atau pustaka bersama) yang dibacanya sebelum proses lainnya.
Sebaliknya, proses lain mungkin dikenai biaya untuk halaman yang juga digunakan
aplikasi Anda. Perkirakan perbedaan kecil antara perangkat dan operasi untuk memori yang didukung file, dan andalkan
anondan swap saat Anda membandingkan.
PSS (Proportional Set Size)
- Paling cocok untuk: Mengambil snapshot memori sistem lengkap dari perangkat tertentu pada waktu tertentu.
- Alasan: PSS memiliki properti matematika yang berguna, yaitu jumlah PSS untuk semua proses yang berjalan sama dengan total memori yang digunakan oleh proses dalam sistem. PSS membagi memori bersama dengan sempurna tanpa penghitungan ganda.
- Kapan harus dihindari: Anda tidak boleh menggunakan PSS untuk membandingkan proses tertentu di dua snapshot yang berbeda, pada waktu yang berbeda, atau di perangkat yang berbeda. Karena PSS bergantung pada jumlah proses lain yang saat ini berbagi halaman dengan proses Anda, PSS aplikasi Anda dapat berfluktuasi meskipun perilaku sebenarnya aplikasi Anda tidak berubah sama sekali.
RSS (Resident Set Size)
- Paling cocok untuk: Melacak penggunaan memori aplikasi individual dari waktu ke waktu, di berbagai Perjalanan Penting Pengguna (CUJ), atau membandingkan antar-versi aplikasi. Ini juga merupakan pilihan paling praktis untuk telemetri lapangan.
- Alasan: RSS lebih stabil daripada PSS. Jumlah ini memberi tahu Anda tentang memori yang dipetakan ke dalam proses Anda, terlepas dari apa yang dilakukan aplikasi lain. Selain itu, RSS murah untuk dikumpulkan. Mengukur PSS atau USS memerlukan pemindaian struktur manajemen memori kernel yang kompleks dan mendapatkan kunci, yang dapat menyebabkan masalah performa sistem (seperti jank UI) jika dikumpulkan terlalu sering.
- Praktik Terbaik Pengukuran Lapangan: Saat mengumpulkan data dari perangkat di
lapangan, biasanya sebaiknya ukur RSS anonim + swap (ZRAM).
- Mengapa hanya anonim? Halaman yang didukung file (seperti kode, resource aplikasi, font, atau file lain yang dipetakan memori) dapat dikeluarkan oleh kernel kapan saja karena tekanan pagecache di seluruh sistem. Menyertakan halaman file dalam metrik untuk melacak aplikasi tertentu akan menimbulkan derau dari keputusan pengelolaan memori OS yang mungkin didorong oleh tekanan memori dari aplikasi lain atau sistem. Sebaliknya, memori anonim (seperti heap Java dan native) dikontrol langsung oleh aplikasi Anda.
- Mengapa menambahkan swap? Saat memori penuh, OS akan mengompresi halaman anonim dan memindahkannya ke ZRAM (swap). Jika Anda hanya mengukur memori anonim residen, metrik Anda mungkin salah menunjukkan "penurunan" dalam penggunaan memori hanya karena sistem berada di bawah tekanan dan menukar halaman Anda. Menambahkan memori yang di-swap akan memastikan Anda memperhitungkan semua memori anonim yang telah dialokasikan aplikasi, baik yang saat ini berada di RAM maupun ZRAM.
- Pilih memcg jika memungkinkan. Di perangkat yang menjalankan Android 17 QPR2 dan
yang lebih tinggi,
anondimemory.statplusmemory.swap.currentmemberi Anda pengukuran serupa yang mengabaikan halaman anonim yang dibagikan dengan Zygote, dan lebih murah untuk dikumpulkan (lihat memcg).
USS (Unique Set Size)
- Paling cocok untuk: Menentukan dampak sistem langsung dari penghentian proses.
- Alasan: USS secara akurat menunjukkan jumlah memori yang akan segera diklaim kembali dan dikembalikan ke sistem jika proses dihentikan (misalnya, oleh daemon Low Memory Killer,
lmkd). - Cara menggunakan: Metrik ini sangat berharga saat mengevaluasi proses persisten atau layanan latar belakang. Jika Anda menilai biaya seluruh sistem dari proses latar belakang, USS akan memberi tahu Anda dengan tepat berapa banyak memori yang dikorbankan sistem secara eksklusif untuk menjaga proses tertentu tetap aktif.