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.
Memantau memori yang tersedia dan penggunaan memori
Anda harus menemukan masalah penggunaan memori aplikasi sebelum dapat memperbaikinya. Memory Profiler di Android Studio membantu Anda menemukan dan mendiagnosis masalah memori melalui cara berikut:
- Lihat bagaimana aplikasi Anda mengalokasikan memori dari waktu ke waktu. Memory Profiler 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 dari heap Java selagi aplikasi berjalan.
- Rekam alokasi memori aplikasi Anda, lalu periksa semua objek yang dialokasikan, lihat stack trace untuk setiap alokasi, dan beralihlah ke kode yang sesuai pada editor Android Studio.
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 secara maksimal dan menghindari penghentian proses aplikasi oleh sistem, Anda dapat mengimplementasikan
antarmuka
ComponentCallbacks2
dalam class Activity
.
Halaman yang disediakan
onTrimMemory()
yang memberi tahu aplikasi tentang siklus proses atau peristiwa terkait memori yang menyajikan
aplikasi Anda untuk mengurangi penggunaan memorinya secara sukarela. Membebaskan memori dapat mengurangi
kemungkinan aplikasi Anda dihentikan oleh
low-memory killer.
Anda dapat menerapkan callback onTrimMemory()
untuk merespons berbagai peristiwa terkait memori, seperti ditunjukkan dalam contoh berikut:
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 Anda miliki pada perangkat saat ini. Untuk mengetahui angkanya, panggil
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
mengakhiri 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; }
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 saat layanan telah menyelesaikan tugasnya. Jika tidak, kebocoran memori dapat terjadi.
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. Keadaan 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, kami merekomendasikan penggunaan 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 digunakan 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 memerlukan biaya yang jauh lebih mahal karena umumnya memerlukan lebih banyak kode yang perlu dijalankan, yang memerlukan lebih banyak waktu dan RAM untuk memetakan kode ke dalam memori. Jika abstraksi Anda tidak memberikan manfaat secara signifikan, hindari abstraksi.
Menggunakan lite protobuf untuk data serial
Buffering protokol (protobuf) adalah mekanisme yang tidak tergantung bahasa, tidak tergantung platform, dan dapat diperluas yang dirancang 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 dapat menyebabkan berbagai masalah pada aplikasi seperti peningkatan penggunaan RAM, peningkatan ukuran APK secara signifikan, dan eksekusi yang lebih lambat.
Untuk mengetahui informasi lebih lanjut, baca readme protobuf.
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 data. Makin banyak waktu yang dihabiskan sistem untuk pembersihan sampah memori, makin cepat daya baterai habis.
Sering kali, churn memori dapat menyebabkan terjadinya sejumlah besar peristiwa pembersihan sampah memori. 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, 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 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 pembersihan sampah memori, kumpulan objek tersebut pada akhirnya meningkatkan jumlah pekerjaan yang diperlukan untuk setiap pemanggilan, karena ini sebanding dengan jumlah byte aktif (yang dapat dijangkau).
Menghapus resource dan library yang boros memori
Beberapa resource dan library dalam kode Anda bisa menghabiskan memori tanpa Anda sadari. Ukuran keseluruhan APK, termasuk library pihak ketiga atau resource tersemat, dapat memengaruhi jumlah memori yang digunakan aplikasi Anda. Anda dapat memperbaiki konsumsi memori aplikasi dengan menghapus komponen, atau resource yang berlebihan, tidak perlu, atau membengkak dari kode Anda.
Mengurangi ukuran APK secara keseluruhan
Anda dapat mengurangi penggunaan memori aplikasi secara signifikan dengan mengurangi ukuran keseluruhan aplikasi. Ukuran bitmap, resource, frame animasi, dan library pihak ketiga dapat berkontribusi pada ukuran aplikasi Anda. Android Studio dan Android SDK menyediakan beberapa alat untuk membantu Anda mengurangi ukuran resource dan dependensi eksternal. Alat ini mendukung metode penyingkatan kode modern, seperti kompilasi R8.
Untuk mengetahui informasi selengkapnya tentang cara mengurangi ukuran aplikasi secara keseluruhan, lihat Mengurangi ukuran aplikasi.
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 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.
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 mengoptimalkannya 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, yang menghasilkan dua implementasi protobuf yang berbeda di 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 dapat membantu menghapus API dan resource dengan tanda yang tepat,
ProGuard tidak dapat menghapus dependensi internal yang besar pada sebuah library. Fitur yang Anda inginkan dalam
library ini mungkin memerlukan dependensi pada tingkat yang lebih rendah. Hal ini akan sangat menyulitkan saat Anda
menggunakan subclass Activity
dari
library—yang mungkin memiliki banyak dependensi—saat library menggunakan refleksi, yang
bersifat umum dan memerlukan penyesuaian ProGuard secara manual agar dapat berfungsi.
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 mempertimbangkan apakah akan menggunakan library, temukan implementasi yang benar-benar cocok dengan kebutuhan Anda. Jika tidak, Anda mungkin perlu membuat implementasi Anda sendiri.