Halaman ini menjelaskan cara mengurangi penggunaan memori dalam aplikasi Anda secara proaktif. Untuk mengetahui informasi tentang cara sistem operasi Android mengelola memori, lihat Ringkasan pengelolaan memori.
Random access memory (RAM) merupakan resource berharga untuk setiap lingkungan pengembangan software, terlebih pada sistem operasi seluler yang sering dibatasi oleh memori fisik. Meskipun Android Runtime (ART) dan mesin virtual Dalvik menjalankan pembersihan sampah memori secara rutin, hal ini tidak berarti Anda dapat mengabaikan kapan dan di mana aplikasi Anda mengalokasikan dan melepaskan memori. Anda tetap perlu menghindari timbulnya kebocoran memori, yang biasanya disebabkan oleh penahanan referensi objek dalam variabel anggota statis, dan melepaskan objek Reference pada waktu yang tepat seperti ditetapkan oleh callback siklus proses.
Mengurangi jejak kode dan resource aplikasi Anda
Beberapa resource dan library dalam kode Anda dapat menghabiskan memori tanpa Anda sadari. Ukuran keseluruhan aplikasi Anda, termasuk library pihak ketiga atau resource tersemat, dapat memengaruhi jumlah memori yang digunakan aplikasi Anda. Anda dapat memperbaiki konsumsi memori aplikasi dengan menghapus komponen, resource, dan library yang berlebihan, tidak perlu, atau membengkak dari kode Anda.
Mengurangi ukuran aplikasi secara keseluruhan dengan mengaktifkan R8
Kode aplikasi yang dikompilasi adalah bagian aktif dari jejak memori runtime Anda. Setiap class, metode, dependensi library, dan konstanta string harus dimuat ke dalam RAM saat dijalankan. Makin besar codebase yang dikompilasi, makin banyak RAM fisik yang diperlukan aplikasi Anda untuk ada.
Anda dapat menggunakan R8 untuk mengurangi jejak memori aplikasi Anda. Meskipun secara tradisional R8 dikenal karena mengecilkan ukuran APK, R8 memiliki dampak positif langsung pada memori runtime (RAM). R8 menganalisis bytecode aplikasi Anda untuk menghapus kode yang tidak terpakai, menggabungkan class yang berlebihan, menyisipkan metode, dan meminimalkan ID. Dengan memuat lebih sedikit bytecode yang dikompilasi dari APK ke dalam RAM, jejak memori dasar aplikasi secara keseluruhan akan berkurang. Selain itu, meminimalkan nama class, metode, dan bidang menjadi ID yang lebih pendek secara langsung mengurangi overhead RAM. Pengoptimalan seperti penggabungan class dan inline metode yang ekstensif juga menggantikan pola alokasi dan pencarian runtime yang mahal, sehingga menghasilkan memori heap dan stack yang dioptimalkan.
Memahami aturan penyimpanan
Aturan keep adalah petunjuk konfigurasi yang memberi tahu R8 bagian kode mana yang harus dipertahankan selama pengoptimalan, sehingga mencegahnya menghapus atau meminifikasi kode yang diandalkan aplikasi Anda. Untuk mengetahui informasi selengkapnya, lihat Ringkasan aturan penyimpanan.
Aturan keep yang ditulis dengan buruk mencegah R8 mengoptimalkan sebagian besar codebase Anda. Hindari aturan penyimpanan yang terlalu luas dan ikuti praktik terbaik berikut:
- Aturan global yang harus dihindari:
-dontoptimize: Menonaktifkan pengoptimalan sepenuhnya untuk seluruh aplikasi, sehingga menghasilkan file yang dapat dieksekusi lebih besar dan lebih lambat.-dontshrink: Mencegah penghapusan kode dan resource yang tidak digunakan.-dontobfuscate: Mencegah minifikasi nama, sehingga tidak mendapatkan penghematan memori yang berharga (terutama di aplikasi besar).
Hindari karakter pengganti di seluruh paket: Aturan luas seperti
-keep class com.example.package.** { *; }memaksa R8 untuk mempertahankan setiap class, kolom, dan metode dalam paket tersebut. Hal ini akan sepenuhnya menghentikan kemampuan R8 untuk menghapus, mengoptimalkan, atau mengecilkan kode dalam paket tersebut.Gunakan file konfigurasi R8 default: Selalu gunakan
proguard-android-optimize.txt.
Untuk mengetahui informasi selengkapnya tentang cara menulis aturan penyimpanan, lihat Ringkasan aturan penyimpanan. Untuk mengetahui pola spesifik yang harus digunakan dan dihindari, lihat Praktik terbaik aturan penyimpanan.
Penganalisis Konfigurasi R8 memberikan insight tentang konfigurasi R8 dan pengaruh setiap aturan keep terhadap aplikasi Anda. Untuk mengetahui informasi selengkapnya tentang cara mengidentifikasi aturan yang memblokir pengoptimalan, lihat Penganalisis Konfigurasi R8.
Hati-hati saat menggunakan library eksternal
Kode library eksternal sering kali tidak ditulis untuk lingkungan seluler dan dapat menjadi tidak efisien saat digunakan untuk bekerja pada klien seluler. Saat menggunakan library eksternal, Anda mungkin perlu mengoptimalkan library tersebut untuk perangkat seluler. Rencanakan pekerjaan ini sebelumnya dan analisis library dalam hal ukuran kode dan jejak RAM sebelum menggunakannya.
Bahkan beberapa library yang dioptimalkan untuk lingkungan seluler pun dapat menyebabkan masalah karena implementasinya yang berbeda. Misalnya, satu library mungkin menggunakan lite protobuf sementara library lain menggunakan micro protobuf, sehingga terdapat dua implementasi protobuf berbeda dalam aplikasi Anda. Hal ini dapat terjadi dengan berbagai implementasi logging, analitik, framework pemuatan gambar, penyimpanan cache, dan berbagai hal lainnya yang tidak Anda harapkan.
Meskipun mengoptimalkan aplikasi menggunakan R8 dapat menghapus kode yang tidak digunakan dari dependensi, efektivitasnya sering kali dibatasi oleh konfigurasi internal library. Misalnya, aturan keep yang luas atau penggunaan refleksi dalam library dapat mencegah R8 menyusutkan kodenya, sehingga menghasilkan footprint memori yang lebih besar. Untuk mengetahui strategi memilih library yang efisien, lihat Pilih library dengan bijak.
Selain itu, hindari menggunakan library bersama untuk hanya satu atau dua fitur dari lusinan fitur lainnya. Jangan menarik sejumlah besar kode dan overhead yang tidak Anda gunakan. Saat Anda mempertimbangkan apakah akan menggunakan library, cari implementasi yang sangat cocok dengan kebutuhan Anda. Jika tidak, Anda mungkin perlu membuat implementasi Anda sendiri.
Menggunakan Hilt atau Dagger 2 untuk injeksi dependensi
Framework injeksi dependensi dapat menyederhanakan kode yang Anda tulis dan memberikan lingkungan adaptif yang berguna untuk pengujian dan perubahan konfigurasi lainnya.
Jika Anda ingin menggunakan framework injeksi dependensi di aplikasi Anda, pertimbangkan untuk menggunakan Hilt atau Dagger. Hilt adalah library injeksi dependensi untuk Android yang berjalan di atas Dagger. Dagger tidak menggunakan refleksi untuk memindai kode aplikasi Anda. Anda dapat menggunakan implementasi waktu kompilasi statis Dagger di aplikasi Android tanpa biaya runtime atau penggunaan memori yang tidak perlu.
Framework injeksi dependensi lain yang menggunakan refleksi menginisialisasi proses dengan memindai kode untuk menemukan anotasi. Proses ini dapat memerlukan siklus CPU dan RAM yang jauh lebih banyak, dan dapat menyebabkan keterlambatan yang kentara saat aplikasi diluncurkan.
Saat menggunakan injeksi dependensi, berhati-hatilah agar tidak terjadi kebocoran memori dengan memastikan bahwa objek dicakup dengan tepat. Mempertahankan objek lebih lama dari yang diperlukan dengan mengikatnya ke siklus proses yang salah dapat menyebabkan kebocoran memori.
Memuat gambar dengan sengaja
Bitmap grafis biasanya merupakan objek umum terbesar yang berada di memori aplikasi Anda. Meskipun Anda bekerja dengan file terkompresi seperti JPEG, file harus dikembangkan menjadi bitmap yang tidak dikompresi untuk ditampilkan di layar. File gambar terkompresi berukuran kecil dapat diperluas menjadi bitmap yang sangat besar.
Misalnya, sebagian besar bitmap menggunakan konfigurasi ARGB_8888, yang berarti setiap
piksel memerlukan memori 4 byte--masing-masing satu byte untuk merah, hijau, biru, dan alfa
(transparansi). Jika Anda memiliki JPEG 100 KB dan menampilkannya dalam tampilan 1000×1000 piksel, bitmap akan memerlukan 4 byte untuk setiap 1.000.000 piksel tersebut, sehingga menambah penggunaan memori hingga 4 MB.
Ada beberapa hal yang dapat Anda lakukan untuk mengoptimalkan penggunaan gambar. Misalnya, menggunakan library pemuatan gambar dapat membantu Anda melepaskan memori saat tidak diperlukan. Untuk mengetahui informasi tentang cara menangani gambar secara efisien, lihat Mengoptimalkan gambar bitmap.
Memantau memori yang tersedia dan penggunaan memori
Anda harus menemukan masalah penggunaan memori aplikasi sebelum dapat memperbaikinya. Profiler memori Android Studio membantu Anda menemukan dan mendiagnosis masalah memori melalui cara berikut:
- Lihat bagaimana aplikasi Anda mengalokasikan memori dari waktu ke waktu. Profiler memori menampilkan grafik real time yang menunjukkan banyaknya memori yang digunakan aplikasi Anda, jumlah objek Java yang dialokasikan, dan kapan pembersihan sampah memori dilakukan.
- Mulai peristiwa pembersihan sampah memori dan ambil cuplikan heap Java selagi aplikasi Anda berjalan.
- Rekam alokasi memori aplikasi Anda, periksa semua objek yang dialokasikan, lihat pelacakan tumpukan untuk setiap alokasi, dan beralihlah ke kode yang sesuai di editor Android Studio.
Profiler memori juga terintegrasi dengan library deteksi kebocoran LeakCanary. Dengan menggunakan LeakCanary, Anda dapat memindahkan analisis kebocoran memori dari perangkat pengujian ke mesin pengembangan, yang dapat mempercepat alur kerja Anda secara signifikan. Untuk mengetahui informasi selengkapnya, lihat catatan rilis Android Studio.
Ada alat lain yang dapat Anda gunakan untuk mendiagnosis masalah memori berdasarkan data dari pengguna yang menjalankan aplikasi produksi Anda:
- Gunakan Android Vitals untuk melacak peristiwa low memory kill (LMK).
- Gunakan Pengelola Profiling untuk melacak error kehabisan memori, serta perilaku aplikasi yang tidak normal yang mungkin disebabkan oleh kebocoran memori.
Melepaskan memori sebagai respons terhadap peristiwa
Android dapat memperoleh kembali memori dari aplikasi Anda atau menghentikan aplikasi sepenuhnya jika diperlukan
untuk mengosongkan memori untuk tugas penting, seperti yang dijelaskan dalam Ringkasan pengelolaan memori. Untuk menyeimbangkan memori sistem lebih jauh lagi dan menghindari penghentian proses aplikasi oleh sistem, Anda dapat mengimplementasikan antarmuka ComponentCallbacks2 dalam class Activity. Metode callback onTrimMemory() yang disediakan memberi tahu aplikasi Anda tentang peristiwa siklus proses atau terkait memori yang memberikan peluang bagus bagi aplikasi Anda untuk mengurangi penggunaan memori secara sukarela.
Mengosongkan memori dapat mengurangi frekuensi aplikasi Anda dihentikan oleh
low-memory killer.
Penerapan onTrimMemory() harus berfokus secara eksklusif pada
peristiwa TRIM_MEMORY_UI_HIDDEN dan TRIM_MEMORY_BACKGROUND. (Mulai
Android 14, sistem tidak lagi mengirimkan notifikasi untuk konstanta lama lainnya. Konstanta tersebut secara resmi tidak digunakan lagi di Android 15.)
TRIM_MEMORY_UI_HIDDEN: Sinyal ini menunjukkan bahwa UI aplikasi Anda telah beralih keluar dari tampilan pengguna. Transisi ini memberikan peluang untuk melepaskan alokasi memori yang besar yang terkait secara ketat dengan UI, seperti bitmap, buffer pemutaran video, atau resource animasi yang kompleks.TRIM_MEMORY_BACKGROUND: Sinyal ini menunjukkan bahwa proses Anda berada di latar belakang dan sekarang menjadi kandidat untuk dihentikan guna memenuhi kebutuhan memori global sistem. Untuk memperpanjang durasi proses Anda tetap dalam status yang di-cache, dan mengurangi jumlah cold start aplikasi, Anda harus melepaskan semua resource secara agresif yang dapat dengan mudah direkonstruksi setelah pengguna melanjutkan sesi mereka.
Contoh kode ini menunjukkan cara mengimplementasikan callback onTrimMemory() untuk merespons
berbagai peristiwa terkait memori:
Kotlin
import android.content.ComponentCallbacks2
// Other import statements.
class MainActivity : AppCompatActivity(), ComponentCallbacks2 {
// Other activity code.
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that is raised.
*/
override fun onTrimMemory(level: Int) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
Java
import android.content.ComponentCallbacks2;
// Other import statements.
public class MainActivity extends AppCompatActivity
implements ComponentCallbacks2 {
// Other activity code.
/**
* Release memory when the UI becomes hidden or when system resources become low.
* @param level the memory-related event that is raised.
*/
public void onTrimMemory(int level) {
if (level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// Release memory related to UI elements, such as bitmap caches.
}
if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
// Release memory related to background processing, such as by
// closing a database connection.
}
}
}
Memeriksa jumlah memori yang Anda perlukan
Agar beberapa proses dapat berjalan sekaligus, Android menetapkan batas pasti ukuran heap yang dialokasikan untuk setiap aplikasi. Batas ukuran heap yang pasti ini bervariasi antarperangkat, tergantung banyaknya RAM yang dimiliki perangkat secara keseluruhan. Jika aplikasi Anda mencapai kapasitas heap dan mencoba mengalokasikan lebih banyak memori, sistem akan menampilkan OutOfMemoryError.
Untuk menghindari kehabisan memori, Anda dapat mengkueri sistem untuk mengetahui banyaknya ruang heap yang tersedia di perangkat saat ini. Anda dapat mengkueri angka ini dari sistem dengan memanggil getMemoryInfo(). Tindakan ini menampilkan objek
ActivityManager.MemoryInfo yang memberikan informasi tentang
status memori saat ini pada perangkat, termasuk memori yang tersedia, memori total, dan
ambang batas memori—tingkat memori di mana sistem akan mulai menghentikan
proses. Objek ActivityManager.MemoryInfo juga mengekspos
lowMemory, yang merupakan boolean sederhana yang memberi tahu Anda apakah perangkat
kehabisan memori.
Contoh cuplikan kode berikut menunjukkan cara menggunakan metode getMemoryInfo()
di aplikasi Anda.
Kotlin
fun doSomethingMemoryIntensive() {
// Before doing something that requires a lot of memory,
// check whether the device is in a low memory state.
if (!getAvailableMemory().lowMemory) {
// Do memory intensive work.
}
}
// Get a MemoryInfo object for the device's current memory status.
private fun getAvailableMemory(): ActivityManager.MemoryInfo {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return ActivityManager.MemoryInfo().also { memoryInfo ->
activityManager.getMemoryInfo(memoryInfo)
}
}
Java
public void doSomethingMemoryIntensive() {
// Before doing something that requires a lot of memory,
// check whether the device is in a low memory state.
ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
if (!memoryInfo.lowMemory) {
// Do memory intensive work.
}
}
// Get a MemoryInfo object for the device's current memory status.
private ActivityManager.MemoryInfo getAvailableMemory() {
ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
return memoryInfo;
}
Memantau penghentian karena memori rendah
Low-memory kill (LMK) yang terlihat oleh pengguna terjadi saat memori sistem sangat menipis. Saat memori hampir penuh, lmkd (daemon penghentian proses karena memori hampir penuh) akan menghentikan proses berdasarkan oom_adj_score-nya. Aplikasi yang di-cache, atau menjalankan layanan tanpa UI terkait (seperti tugas), memiliki skor tertinggi dan dihentikan terlebih dahulu. Jika memori tetap sangat rendah, daemon akan dipaksa untuk merebut kembali memori dari proses dengan oom_adj_score 0.
Karena skor tersebut dicadangkan untuk aplikasi yang terlihat, penghentiannya akan menyebabkan
keluar dari proses yang tidak lancar dan langsung. Bagi pengguna akhir, aplikasi tampak error, sering kali melewati mekanisme penyimpanan status siklus proses standar dan mengakibatkan hilangnya progres pengguna.
Penghentian proses latar depan menjadi fokus utama di Android Vitals karena berfungsi sebagai pengganti yang akurat untuk salah pengelolaan memori. Meskipun rasio LMK yang lebih tinggi dari 1% menunjukkan kebutuhan mendesak untuk segera mengambil tindakan, rasio yang rendah tidak selalu merupakan indikator kesehatan. Rasio LMK yang dirasakan pengguna yang rendah mungkin berarti daemon LMK sering menghentikan proses saat berada di latar belakang, yang menurunkan performa "start-up hangat" dan kelancaran multitasking. Oleh karena itu, sebaiknya ikuti praktik terbaik memori terlepas dari skor LMK Anda saat ini, untuk memastikan stabilitas jangka panjang dan kesehatan perangkat.
Menggunakan ProfilingManager untuk melacak masalah memori
Platform Android menyediakan
ProfilingManager, API pengamatan lanjutan yang memungkinkan Anda merekam data pengguna dalam produksi berdasarkan pemicu yang Anda tetapkan. Tindakan ini dapat membantu Anda mengidentifikasi masalah memori yang sulit direproduksi.
Dua pemicu baru yang diperkenalkan dengan Android 17 sangat berguna untuk mendeteksi masalah memori:
TRIGGER_TYPE_OOMmenunjukkan bahwa aplikasi telah memunculkanOutOfMemoryError. Hal ini dipicu pada saat aplikasi berikutnya dimulai setelah error, saat aplikasi mendaftar untuk pemicu pembuatan profil.TRIGGER_TYPE_ANOMALYdipicu saat sistem mendeteksi perilaku anomali dari aplikasi. Selain hal lainnya, hal ini dapat dipicu oleh penggunaan memori yang berlebihan. Peringatan ini dipicu setelah aplikasi menunjukkan penggunaan memori yang berlebihan, dan sebelum sistem melakukan tindakan apa pun untuk menghentikan proses yang bermasalah. Misalnya, jika aplikasi melebihi batas memori yang diperkenalkan di Android 17,TRIGGER_TYPE_ANOMALYakan dipicu sebelum sistem menghentikan aplikasi.
Untuk mengetahui informasi selengkapnya tentang penggunaan ProfilingManager untuk mendaftarkan
dan mengambil pemicu secara terprogram, lihat dokumentasi
pembuatan profil berbasis pemicu. Firebase Crashlytics juga mendukung pemicu
TRIGGER_TYPE_OOM dan TRIGGER_TYPE_ANOMALY, yang mengorelasikannya dengan
metadata diagnostik lainnya.
Anda juga dapat menggunakan pembuatan profil yang didorong aplikasi untuk menentukan titik awal dan akhir rekaman aktivitas secara manual. Sebaiknya lakukan hal ini untuk mengambil dump heap atau profil heap secara manual di area yang Anda duga mungkin mengalami kebocoran memori atau penggunaan memori yang berlebihan.
Melacak status dan memori proses
Selain callback dan peristiwa memori, Anda dapat secara eksplisit mengkueri status dan prioritas proses aplikasi Anda. Hal ini sangat berguna untuk memahami cara sistem operasi mengklasifikasikan status aplikasi Anda saat ini (misalnya, apakah aplikasi Anda berada di latar depan, dirasakan pengguna, atau di-cache) dan apakah aplikasi berisiko dihentikan oleh proses penghentian aplikasi karena memori rendah.
Dengan dasbor python untuk pemantauan berkelanjutan
Gunakan skrip python Android Memory Monitor saat menjalankan aplikasi untuk melihat pemantauan berkelanjutan terhadap proses dan jejak memori aplikasi Anda. Ini adalah
alat command line yang menggunakan perintah adb untuk menampilkan dasbor terminal dan web
guna membantu Anda memahami status dan penggunaan memori proses aplikasi Anda. Untuk meluncurkan dasbor, jalankan skrip dengan nama paket aplikasi Anda, misalnya:
python3 android_mem_monitor.py your.package.name
Untuk mengetahui petunjuk mendetail tentang fitur dan penggunaan dasbor, lihat file README atau komentar inline skrip.
Saat runtime dengan ActivityManager
Untuk mengambil informasi tentang proses yang sedang berjalan saat runtime, Anda dapat membuat kueri
ActivityManager.getRunningAppProcesses(). Tindakan ini akan menampilkan daftar objek
yang berisi informasi dasar tentang setiap proses aplikasi Anda. Kolom
importance menunjukkan tingkat kepentingan relatif yang diberikan sistem
pada proses, dan dipetakan ke konstanta seperti:
IMPORTANCE_FOREGROUND: Proses sedang menjalankan UI latar depan secara aktif.IMPORTANCE_FOREGROUND_SERVICE: Proses menjalankan layanan latar depan (seperti musik latar belakang aktif atau download besar), yang merupakan contoh layanan yang dirasakan pengguna.IMPORTANCE_CACHED: Proses ini berisi kode yang di-cache. Proses dibekukan dan memori yang tidak digunakan diklaim kembali oleh Android. Dalam status ini, OSMemoryLimitertidak akan menghentikan aplikasi karena melampaui batas memori khusus aplikasi. Namun, jika keseluruhan sistem kehabisan memori, aplikasi adalah kandidat utama untuk dihentikan oleh proses memori rendah.
Untuk melakukan debug dengan adb
Saat men-debug perilaku memori dan siklus proses, Anda juga dapat menggunakan perintah
adb untuk membantu menyelidiki skor OOM dan jejak memori aplikasi secara real-time. Untuk memantau proses dan status memori aplikasi, Anda
dapat menggunakan perintah berikut:
- Temukan ID proses (PID) aktif: Pertama, temukan semua proses yang sedang dijalankan aplikasi Anda menggunakan perintah
psyang difilter menurut nama paket Anda.
shell adb shell ps -A | grep your.package.name - Periksa Skor OOM saat ini: Gunakan PID proses untuk membaca skor OOM-nya dari sistem file proses. Skor 0-199 biasanya menunjukkan aplikasi yang berada di latar depan, 200-249 mewakili layanan yang dapat dirasakan, 250-899 adalah untuk layanan latar belakang, dan skor 900+ berarti proses di-cache dan kemungkinan akan dihentikan.
shell adb shell cat /proc/pid/oom_score_adj - Pantau Skor OOM secara berkelanjutan: Untuk memantau perubahan pada Skor OOM aplikasi secara berkelanjutan (yang akan menjadi Skor OOM terendah di antara semua prosesnya), Anda dapat menggunakan perintah
watch-uids.
shell adb shell am watch-uids - Ukur jejak memori: Untuk mendapatkan pembacaan cepat jejak memori inti proses, seperti Penggunaan memori RSS Anonim dan Swap, baca statusnya langsung dari sistem file proses Linux.
shell adb shell cat /proc/pid/status | grep -E "VmRSS|RssAnon|VmSwap"
Untuk melihat perincian alokasi memori yang lebih mendetail dan komprehensif, gunakan perintahdumpsys meminfo:
shell adb shell dumpsys meminfo pid - Menyelidiki komponen aktif: Jika proses Anda mempertahankan prioritas yang sangat tinggi secara tidak terduga (seperti tetap berada pada skor OOM
200, bukan turun ke yang di-cache), Anda dapat memeriksa secara tepat apa yang dijalankan oleh proses tersebut. Perintahdumpsys activity processesmengungkapkan Aktivitas, Layanan, Penyedia, dan Penerima tertentu yang membuat proses tetap berjalan.
shell adb shell dumpsys activity processes your.package.name
Menggunakan konstruksi kode yang lebih hemat-memori
Beberapa fitur Android, class Java, dan konstruksi kode menggunakan lebih banyak memori dibandingkan yang lain. Anda dapat meminimalkan banyaknya memori yang digunakan aplikasi dengan memilih alternatif yang lebih efisien dalam kode Anda.
Menggunakan layanan seperlunya
Sebaiknya jangan biarkan layanan berjalan jika tidak diperlukan. Membiarkan layanan yang tidak perlu tetap berjalan adalah salah satu kesalahan pengelolaan memori terburuk yang dapat dilakukan aplikasi Android. Jika aplikasi Anda memerlukan layanan untuk bekerja di latar belakang, jangan biarkan layanan tersebut terus berjalan kecuali jika diperlukan untuk menjalankan tugas. Hentikan layanan saat layanan telah menyelesaikan tugasnya. Jika tidak, Anda berisiko menimbulkan kebocoran memori.
Saat Anda memulai layanan, sistem lebih suka mempertahankan agar proses untuk layanan tersebut selalu berjalan. Perilaku ini membuat proses layanan menjadi sangat tidak efisien karena RAM yang digunakan oleh layanan menjadi tidak tersedia untuk proses lainnya. Hal ini mengurangi jumlah proses yang di-cache yang dapat dipertahankan sistem dalam cache LRU, sehingga peralihan aplikasi menjadi kurang efisien. Selain itu, tindakan ini dapat membebani sistem saat memori yang tersedia sangat terbatas dan sistem tidak dapat mempertahankan cukup proses untuk meng-hosting semua layanan yang sedang berjalan.
Secara umum, hindari penggunaan layanan persisten karena jenis layanan ini terus-menerus membebani memori yang tersedia. Sebagai gantinya, sebaiknya gunakan implementasi alternatif, seperti WorkManager.
Untuk mengetahui informasi selengkapnya tentang cara menggunakan WorkManager guna menjadwalkan proses latar belakang, lihat Pekerjaan persisten.
Menggunakan container data yang dioptimalkan
Beberapa class yang disediakan oleh bahasa pemrograman tidak dioptimalkan untuk
penggunaan pada perangkat seluler. Misalnya, implementasi
HashMap generik bisa sangat menguras memori
karena memerlukan objek entri tersendiri untuk setiap pemetaan.
Framework Android mencakup beberapa penampung data yang dioptimalkan, termasuk
SparseArray,
SparseBooleanArray, dan
LongSparseArray. Misalnya, class SparseArray lebih efisien karena dapat mencegah perlunya sistem melakukan autobox pada kunci dan terkadang nilai, yang membuat satu objek lagi atau dua objek per entri.
Jika perlu, Anda dapat beralih ke array mentah kapan saja untuk menggunakan struktur data yang benar-benar ramping.
Hati-hati dengan abstraksi kode
Developer sering menggunakan abstraksi sebagai praktik pemrograman yang baik karena dapat meningkatkan fleksibilitas dan pemeliharaan kode. Namun, abstraksi umumnya memerlukan lebih banyak kode untuk dieksekusi. Seperti yang dijelaskan dalam Mengurangi jejak kode dan resource aplikasi Anda, codebase yang dikompilasi lebih besar secara langsung akan meningkatkan RAM fisik yang diperlukan aplikasi Anda. Jika abstraksi Anda tidak memberikan manfaat secara signifikan, hindari abstraksi.
Menggunakan lite protobuf untuk data serial
Buffering protokol (protobuf) adalah mekanisme yang tidak bergantung pada bahasa, tidak bergantung pada platform, dan dapat diperluas yang dirancang oleh Google untuk membuat serialisasi data terstruktur—mirip dengan XML, tetapi lebih kecil, lebih cepat, dan lebih sederhana. Jika menggunakan protobuf untuk data Anda, selalu gunakan lite protobuf dalam kode sisi klien. Protobuf reguler menghasilkan kode yang sangat panjang, yang meningkatkan jejak kode aplikasi Anda di RAM (lihat Mengurangi jejak kode dan resource aplikasi Anda) dan berkontribusi pada peningkatan ukuran APK.
Untuk mengetahui informasi selengkapnya, lihat readme protobuf.
Berhati-hatilah terhadap kebocoran memori
Pengelolaan referensi yang tidak tepat dapat menyebabkan kebocoran memori saat objek bertahan lebih lama dari masa pakainya, sehingga mencegah pembersih sampah memori mengklaim kembali memori objek yang bocor. Untuk menghindari kebocoran memori, terapkan desain yang kompatibel dengan siklus proses.
Menghindari churn memori
Peristiwa pembersihan sampah memori tidak memengaruhi performa aplikasi. Namun, banyaknya peristiwa pembersihan sampah memori yang terjadi dalam waktu singkat dapat dengan cepat menghabiskan daya baterai serta sedikit meningkatkan waktu untuk menyiapkan frame karena interaksi yang diperlukan antara pembersih sampah memori dan thread aplikasi. Makin banyak waktu yang dihabiskan sistem untuk pembersihan sampah memori, makin cepat daya baterai habis.
Sering kali, churn memori dapat menyebabkan sejumlah besar peristiwa pembersihan sampah memori terjadi. Dalam praktiknya, churn memori menunjukkan jumlah pengalokasian objek sementara yang terjadi dalam rentang waktu tertentu.
Misalnya, Anda dapat mengalokasikan beberapa objek sementara dalam loop for.
Atau, Anda dapat membuat objek Paint atau
Bitmap baru di dalam
fungsi onDraw()
tampilan. Dalam kedua kasus ini, aplikasi akan membuat banyak objek dengan cepat pada
volume tinggi. Hal ini dapat menghabiskan semua memori yang tersedia dengan cepat di generasi muda, sehingga memaksa terjadinya peristiwa pembersihan sampah memori.
Gunakan Memory Profiler untuk menemukan tempat dalam kode dengan churn memori yang tinggi agar Anda dapat memperbaikinya.
Setelah mengidentifikasi area masalah dalam kode Anda, cobalah untuk mengurangi jumlah alokasi dalam area yang kritis performa. Pertimbangkan untuk mengeluarkan objek dari loop dalam, atau mungkin memindahkannya ke dalam struktur alokasi berbasis factory.
Anda juga dapat mengevaluasi apakah kumpulan objek menguntungkan kasus penggunaan. Dengan kumpulan objek, alih-alih melepaskan instance objek ke lantai, Anda akan melepaskannya ke dalam kumpulan setelah tidak diperlukan lagi. Saat berikutnya instance objek dari jenis tersebut diperlukan, Anda dapat memperolehnya dari kumpulan, bukan mengalokasikannya.
Evaluasi performa secara menyeluruh untuk menentukan apakah kumpulan objek cocok dalam situasi tertentu. Ada kalanya kumpulan objek dapat memperburuk performa. Meskipun menghindari alokasi, kumpulan tersebut menimbulkan overhead lain. Misalnya, mempertahankan kumpulan biasanya melibatkan sinkronisasi, yang memiliki overhead yang tidak dapat diabaikan. Selain itu, menghapus instance objek gabungan untuk menghindari kebocoran memori selama rilis, lalu inisialisasinya selama akuisisi dapat menimbulkan overhead yang bukan nol.
Menahan lebih banyak instance objek di dalam kumpulan daripada yang diperlukan juga dapat membebani pembersihan sampah memori. Meskipun kumpulan objek mengurangi jumlah pemanggilan pengumpulan sampah, kumpulan objek tersebut pada akhirnya meningkatkan jumlah pekerjaan yang diperlukan untuk setiap pemanggilan, karena ini sebanding dengan jumlah byte aktif (yang dapat dijangkau).