Google berkomitmen untuk mendorong terwujudnya keadilan rasial bagi komunitas Kulit Hitam. Lihat caranya.

Mengelola memori aplikasi

Random access memory (RAM) merupakan resource berharga dalam setiap lingkungan pengembangan software, terlebih pada sistem operasi seluler yang sering dibatasi oleh memori fisik. Meskipun baik Android Runtime (ART) maupun 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.

Halaman ini menjelaskan cara mengurangi penggunaan memori secara proaktif dalam aplikasi Anda. Untuk informasi tentang cara sistem operasi Android mengelola memori, lihat Ringkasan Pengelolaan Memori Android.

Memantau memori yang tersedia dan penggunaan memori

Agar dapat memperbaiki masalah penggunaan memori pada aplikasi, Anda harus menemukan masalah tersebut terlebih dahulu. Memory Profiler di Android Studio membantu Anda menemukan dan mendiagnosis masalah memori melalui cara berikut:

  1. 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.
  2. Mulai peristiwa pembersihan sampah memori dan ambil cuplikan dari heap Java selagi aplikasi berjalan.
  3. Rekam alokasi memori aplikasi Anda, lalu periksa semua objek yang dialokasikan, lihat pelacakan tumpukan untuk setiap alokasi, dan beralihlah ke kode yang sesuai pada editor Android Studio.

Melepaskan memori sebagai respons terhadap peristiwa

Seperti dijelaskan dalam Ringkasan Pengelolaan Memori Android, Android dapat mendapatkan kembali memori dari aplikasi Anda melalui beberapa cara atau, jika perlu, mengakhiri aplikasi Anda sepenuhnya guna mengosongkan memori untuk tugas-tugas penting. Untuk menyeimbangkan memori sistem lebih jauh lagi dan menghindari pengakhiran proses aplikasi oleh sistem, Anda dapat mengimplementasikan antarmuka ComponentCallbacks2 dalam class Activity. Metode callback onTrimMemory() yang diberikan memungkinkan aplikasi Anda untuk mendeteksi peristiwa terkait memori saat aplikasi Anda berada di latar depan atau latar belakang, dan kemudian melepaskan objek sebagai respons terhadap peristiwa siklus proses aplikasi atau sistem yang mengindikasikan bahwa sistem perlu mendapatkan kembali memori.

Misalnya, Anda dapat mengimplementasikan callback onTrimMemory() untuk merespons berbagai peristiwa terkait memori seperti yang ditunjukkan di sini:

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 was raised.
         */
        override fun onTrimMemory(level: Int) {

            // Determine which lifecycle or system event was raised.
            when (level) {

                ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN -> {
                    /*
                       Release any UI objects that currently hold memory.

                       The user interface has moved to the background.
                    */
                }

                ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW,
                ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL -> {
                    /*
                       Release any memory that your app doesn't need to run.

                       The device is running low on memory while the app is running.
                       The event raised indicates the severity of the memory-related event.
                       If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
                       begin killing background processes.
                    */
                }

                ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
                ComponentCallbacks2.TRIM_MEMORY_MODERATE,
                ComponentCallbacks2.TRIM_MEMORY_COMPLETE -> {
                    /*
                       Release as much memory as the process can.

                       The app is on the LRU list and the system is running low on memory.
                       The event raised indicates where the app sits within the LRU list.
                       If the event is TRIM_MEMORY_COMPLETE, the process will be one of
                       the first to be terminated.
                    */
                }

                else -> {
                    /*
                      Release any non-critical data structures.

                      The app received an unrecognized memory level value
                      from the system. Treat this as a generic low-memory message.
                    */
                }
            }
        }
    }
    

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 was raised.
         */
        public void onTrimMemory(int level) {

            // Determine which lifecycle or system event was raised.
            switch (level) {

                case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:

                    /*
                       Release any UI objects that currently hold memory.

                       The user interface has moved to the background.
                    */

                    break;

                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
                case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:

                    /*
                       Release any memory that your app doesn't need to run.

                       The device is running low on memory while the app is running.
                       The event raised indicates the severity of the memory-related event.
                       If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
                       begin killing background processes.
                    */

                    break;

                case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
                case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
                case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:

                    /*
                       Release as much memory as the process can.

                       The app is on the LRU list and the system is running low on memory.
                       The event raised indicates where the app sits within the LRU list.
                       If the event is TRIM_MEMORY_COMPLETE, the process will be one of
                       the first to be terminated.
                    */

                    break;

                default:
                    /*
                      Release any non-critical data structures.

                      The app received an unrecognized memory level value
                      from the system. Treat this as a generic low-memory message.
                    */
                    break;
            }
        }
    }
    

Callback onTrimMemory() ditambahkan di Android 4.0 (API level 14). Untuk versi sebelumnya, Anda dapat menggunakan onLowMemory() yang kira-kira setara dengan peristiwa TRIM_MEMORY_COMPLETE.

Memeriksa banyaknya memori yang sebaiknya digunakan

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 telah 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(). Ini mengembalikan 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 menampilkan boolean sederhana, lowMemory yang memberi tahu Anda apakah perangkat kehabisan memori.

Cuplikan kode berikut menunjukkan contoh bagaimana Anda dapat menggunakan metode getMemoryInfo() pada aplikasi Anda.

Kotlin

    fun doSomethingMemoryIntensive() {

        // Before doing something that requires a lot of memory,
        // check to see 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 to see 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 cenderung 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

Membiarkan layanan berjalan saat tidak diperlukan merupakan salah satu kesalahan pengelolaan memori terburuk yang bisa dilakukan aplikasi Android. Jika aplikasi Anda memerlukan layanan untuk menjalankan tugas di latar belakang, jangan biarkan layanan tersebut terus berjalan kecuali jika diperlukan untuk menjalankan tugas. Ingatlah untuk menghentikan layanan saat tugasnya selesai. Jika tidak, Anda berisiko menimbulkan kebocoran memori secara tidak sengaja.

Saat Anda memulai layanan, sistem lebih suka mempertahankan agar proses untuk layanan tersebut selalu berjalan. Perilaku ini menjadikan proses layanan sangat tidak efisien karena RAM yang digunakan oleh layanan tidak tersedia untuk proses-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, sebaiknya Anda menghindari penggunaan layanan persisten karena jenis layanan ini terus-menerus membebani memori yang tersedia. Sebagai gantinya, kami merekomendasikan penggunaan implementasi alternatif seperti JobScheduler. Untuk informasi selengkapnya tentang cara menggunakan JobScheduler untuk menjadwalkan proses latar belakang, lihat Pengoptimalan Latar Belakang.

Jika Anda harus menggunakan layanan, cara terbaik untuk membatasi masa aktif layanan adalah dengan menggunakan IntentService, yang akan menutup dengan sendirinya begitu selesai menangani intent yang memulainya. Untuk informasi lebih lanjut, baca Menjalankan Layanan di Latar Belakang.

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 container 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 abstraksi dapat meningkatkan fleksibilitas dan pemeliharaan kode. Namun, abstraksi memunculkan risiko signifikan: biasanya, abstraksi memerlukan eksekusi kode yang lebih banyak, yang berarti memerlukan lebih banyak waktu dan lebih banyak RAM agar kode dapat dipetakan ke dalam memori. Karena itu, jika abstraksi Anda tidak memberikan manfaat signifikan, sebaiknya Anda menghindarinya.

Menggunakan lite protobuf untuk data serial

Protocol buffer (protobuf) adalah mekanisme 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 ingin menggunakan protobuf untuk data Anda, sebaiknya Anda selalu menggunakan lite protobuf dalam kode sistem 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 informasi selengkapnya, lihat bagian "Versi lite" di protobuf readme.

Menghindari churn memori

Seperti disebutkan sebelumnya, peristiwa pembersihan sampah memori biasanya tidak memengaruhi performa aplikasi. Namun, banyaknya peristiwa pembersihan sampah memori yang terjadi dalam waktu singkat dapat menghabiskan waktu render frame Anda dengan cepat. Semakin banyak waktu yang dihabiskan sistem untuk pembersihan sampah memori, semakin sedikit waktu yang dimilikinya untuk tugas lain seperti rendering atau streaming audio.

Sering kali, churn memori 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, sehingga memaksa terjadinya peristiwa pembersihan sampah memori.

Tentu saja, Anda perlu menemukan area dalam kode dengan churn memori yang tinggi agar dapat mengatasi masalah ini. Untuk itu, sebaiknya Anda menggunakan Memory Profiler di Android Studio.

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.

Menghapus resource dan library yang boros memori

Beberapa resource dan library dalam kode Anda dapat 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, resource, atau library 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 semua library pihak ketiga dapat berkontribusi pada ukuran APK 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. (Android Studio 3.3 dan yang lebih rendah menggunakan ProGuard, bukan kompilasi R8.)

Untuk informasi selengkapnya tentang cara mengurangi ukuran APK secara keseluruhan, lihat panduan cara mengurangi ukuran aplikasi Anda.

Gunakan 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 pada aplikasi Anda, pertimbangkan untuk menggunakan Dagger 2. Dagger tidak menggunakan refleksi untuk memindai kode aplikasi. Dengan implementasi waktu kompilasi statis, Dagger dapat digunakan pada aplikasi Android tanpa biaya runtime atau penggunaan memori yang tidak perlu.

Framework injeksi dependensi lain yang menggunakan refleksi cenderung 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. Jika memutuskan untuk menggunakan library eksternal, Anda mungkin perlu mengoptimalkannya untuk perangkat seluler. Rencanakan tugas tersebut di muka dan analisis library dalam hal ukuran kode dan kebutuhan RAM sebelum memutuskan untuk 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, analisis, 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 tingkat lebih rendah. Hal ini akan sangat menyulitkan jika Anda menggunakan subclass Activity dari sebuah library (yang cenderung memiliki banyak dependensi), jika library menggunakan refleksi (yang umum dan berarti Anda harus menghabiskan banyak waktu untuk mengutak-atik ProGuard secara manual agar berfungsi), dan sebagainya.

Selain itu, hindari menggunakan library bersama untuk hanya satu atau dua fitur dari lusinan fitur lainnya. Anda tentu tidak ingin menulis sejumlah besar kode dan overhead yang bahkan 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.