OutOfMemoryError — pengecualian fatal yang terjadi ketika mesin virtual Java (JVM) atau Android Runtime (ART) tidak dapat mengalokasikan memori untuk objek baru karena kekurangan ruang di Heap. Menurut Square Engineering, 70% OutOfMemoryError di aplikasi seluler disebabkan oleh kebocoran memori, bukan pelampauan batas yang sebenarnya. Memahami penyebab OOM adalah kunci untuk aplikasi yang stabil.
Hal Utama
OutOfMemoryError (OOM) — adalah pengecualian dari keluarga VirtualMachineError di Java/Kotlin yang menandakan ketidakmampuan mengalokasikan memori untuk objek baru. Berbeda dengan pengecualian checked, OOM adalah Error dan tidak memerlukan penanganan melalui catch — meskipun secara teknis bisa ditangkap. Setelah OOM terjadi, aplikasi biasanya berada dalam keadaan tidak stabil dan disarankan untuk menutupnya.
Di Android, setiap aplikasi memiliki batas Heap yang ditetapkan oleh produsen perangkat. Untuk smartphone modern dengan RAM 6+ GB, batasnya adalah 256–512 MB, untuk perangkat anggaran — 128–192 MB. Ketika total volume semua objek hidup melampaui batas ini, ART melemparkan OutOfMemoryError.
Penting untuk dipahami: OOM tidak selalu berarti memori fisik perangkat habis. Ini berarti aplikasi telah menghabiskan batas Heap yang ditetapkan oleh sistem. Aplikasi lain mungkin memiliki memori bebas, tetapi aplikasi Anda tidak dapat menggunakannya karena isolasi proses di Android.
Lima skenario secara teratur menyebabkan OOM di aplikasi seluler. Setiap skenario terkait dengan jenis data atau operasi tertentu.
Bitmap — konsumen memori utama di aplikasi Android. Memuat gambar FullHD (1920 × 1080) dalam ukuran asli memakan 8.3 MB dalam format ARGB_8888. Jika ada 50 gambar seperti itu di RecyclerView — itu 415 MB, yang melampaui Heap perangkat mana pun. Memuat gambar tanpa inSampleSize — OOM terjamin di perangkat lemah.
Gunakan Glide atau Coil untuk penskalaan otomatis. Pustaka ini memuat gambar dalam ukuran yang sesuai dengan View, bukan resolusi asli. Untuk penggunaan langsung BitmapFactory.Options, terapkan inSampleSize: hitung sebagai pangkat dua sehingga ukuran akhir tidak melebihi 2048 × 2048 piksel. Selain itu, gunakan RGB_565 alih-alih ARGB_8888 untuk gambar tanpa transparansi — ini mengurangi konsumsi memori hingga setengahnya.
fun loadScaledBitmap(path: String, reqWidth: Int): Bitmap? {
val opts = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeFile(path, opts)
opts.inSampleSize = calculateSampleSize(opts.outWidth, reqWidth)
opts.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, opts)
}
Satu kebocoran beberapa KB tidak akan menyebabkan OOM. Tetapi puluhan kebocoran di setiap layar terakumulasi: setiap transisi ke layar menambah kebocoran, GC tidak dapat membebaskan objek, Heap terisi. Pola tipikal: pengguna membuka dan menutup layar profil 20 kali → Heap bertambah 200 MB → aplikasi crash dengan OOM.
Pasang LeakCanary di proyek untuk deteksi kebocoran otomatis. Ini akan menampilkan setiap objek yang bocor dengan stack trace yang tepat. Setelah memperbaiki semua kebocoran, konsumsi Heap menjadi stabil: setelah menutup layar, memori kembali ke tingkat dasar.
Memuat file sepenuhnya ke byte[] — jalan langsung menuju OOM. File JSON 50 MB saat parsing akan membuat string dengan ukuran yang sama ditambah model DOM. File video yang dimuat ke memori, buffer audio, dan kumpulan data protobuf besar — semuanya dapat melampaui batas Heap dalam satu operasi.
Proses data besar dengan aliran: InputStream dengan buffer 4–8 KB, parser JSON streaming (Jackson atau Gson dengan JsonReader), MediaCodec untuk video. Jangan pernah memanggil File.readBytes() pada file yang lebih besar dari 10% Heap yang tersedia.
Pembuatan objek secara intensif dalam loop tanpa GC perantara dapat menyebabkan OOM, terutama pada perangkat dengan Heap kecil. Contoh: membuat 100.000 objek dalam for-loop yang tidak muat di Heap sebelum GC sempat mengumpulkannya. Ini lebih sering terjadi di game dan editor grafis.
Gunakan Object Pool untuk objek yang dibuat dan dihancurkan secara massal. Untuk data numerik, gunakan tipe primitif (FloatArray alih-alih List<Float>). RecyclerView dengan ViewHolder Pool memecahkan masalah ini untuk komponen UI.
Fragmentasi — keadaan di mana total memori bebas cukup, tetapi tidak ada blok berkelanjutan untuk objek baru. ART memadatkan Heap saat GC, tetapi tidak selalu berhasil. Array besar (Bitmap, byte[]) paling sensitif terhadap fragmentasi.
ART di Android 8+ menggunakan Generational GC, yang mengurangi fragmentasi dengan memisahkan objek muda dan tua. Meskipun demikian, hindari mengalokasikan fragmen dengan ukuran berbeda di pool yang sama — coba gunakan buffer yang dialokasikan sebelumnya dengan ukuran tetap.
Batas Heap di Android tidak konstan — tergantung pada produsen, model perangkat, dan versi OS. Google menetapkan persyaratan minimum melalui Compatibility Definition Document (CDD), tetapi produsen menetapkan nilai sebenarnya.
| Kategori Perangkat | Heap Khas | largeHeap |
|---|---|---|
| Anggaran (1–2 GB RAM) | 128–192 MB | 256–384 MB |
| Menengah (3–4 GB RAM) | 256–384 MB | 512 MB |
| Flagship (6+ GB RAM) | 384–512 MB | 768 MB–1 GB |
| Tablet (4+ GB RAM) | 256–512 MB | 768 MB |
| Wear OS | 32–64 MB | Tidak ada |
Batas yang ditingkatkan dapat diminta melalui android:largeHeap="true" di manifes. Gunakan dengan hati-hati: meningkatkan Heap tidak menyelesaikan masalah kebocoran dan dapat memperburuk pengalaman pengguna jika sistem terpaksa membunuh aplikasi lain untuk membebaskan memori. Untuk Wear OS, batas Heap minimal — hanya 32–64 MB, di sini largeHeap tidak tersedia, dan penghematan memori menjadi dua kali lipat penting.
Diagnosis OOM memerlukan analisis Heap Dump dan pemahaman objek mana yang mengonsumsi memori. Android Studio menyediakan semua alat yang diperlukan.
Langkah 1: Tangkap momen OOM. Di Android Memory Profiler, tekan Record memory allocations dan jalankan skenario yang menyebabkan crash. Profiler akan menunjukkan lonjakan alokasi sebelum OOM. Jika OOM tidak dapat direproduksi, kurangi Heap melalui android:smallHeap di build debug atau gunakan DDMS dengan panggilan GC manual.
Langkah 2: Ambil Heap Dump pada saat beban puncak (sebelum OOM). Buka Dump di Android Studio: tab Classes diurutkan berdasarkan Retained Size. Objek terbesar — Bitmap, byte[], String. Untuk setiap Bitmap, periksa ukuran (width × height × 4 byte) dan jalur muat melalui Stack Trace.
Langkah 3: Analisis jumlah objek berulang. Jika Anda melihat 200 Fragment atau Activity identik — itu kebocoran. Jika 500 Bitmap dengan ukuran yang sama — masalah cache gambar. MAT (Memory Analyzer Tool) menyediakan analisis lebih dalam dengan Dominator Tree, yang menunjukkan objek mana yang menahan 80% Heap.
// Perintah untuk Heap Dump melalui adb
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.
Strategi komprehensif pencegahan OOM mencakup lima tingkat perlindungan: dari solusi arsitektur hingga pemantauan di produksi.
ViewModel + Repository pola memisahkan data dari UI dan mencegah penahanan View saat rotasi layar. ViewModel bertahan lebih lama dari Activity, datanya tidak hilang, dan View dapat dibuat ulang tanpa duplikasi data di memori. Gunakan StateFlow alih-alih LiveData untuk manajemen status yang eksplisit.
Glide — pustaka wajib untuk bekerja dengan gambar. Glide secara otomatis menskala, menyimpan cache (disk + memori), dan mendaur ulang Bitmap. Konfigurasikan diskCacheStrategy dan skipMemoryCache untuk daftar besar. Untuk gambar animasi, gunakan Glide dengan GIF/WebP — mereka memakan lebih sedikit memori daripada urutan Bitmap.
Firebase Performance Monitoring melacak konsumsi memori secara real-time. Atur peringatan pada penggunaan Heap lebih dari 80% dari batas — ini sinyal untuk pemeriksaan. Crashlytics mengumpulkan OOM sebagai pengecualian dan menunjukkan status Heap terakhir yang diketahui sebelum crash. Untuk Android 11+, gunakan ApplicationExitInfo untuk deteksi penghentian OOM.
Wajib uji aplikasi pada perangkat dengan Heap minimal (128–192 MB). Emulator dengan layar kecil dan Heap kecil mensimulasikan perangkat anggaran. Jika aplikasi berjalan pada perangkat semacam itu, tidak akan ada masalah OOM di flagship. Gunakan Firebase Test Lab dengan perangkat nyata dari berbagai kategori harga.
// Memeriksa Heap yang tersedia sebelum operasi berat
fun canAllocate(requiredBytes: Long): Boolean {
val runtime = Runtime.getRuntime()
val free = runtime.freeMemory()
return free > requiredBytes * 2 // cadangan 50%
}
Pertanyaan yang Sering Diajukan
Secara teknis ya, tetapi tidak disarankan. Setelah OOM, aplikasi berada dalam keadaan tidak stabil: alokasi baru mungkin tidak berfungsi, dan beberapa objek mungkin dibuat sebagian. Satu-satunya tindakan yang masuk akal di catch — pencatatan log dan restart Activity.
Batas Heap berbeda di setiap perangkat. Operasi yang membutuhkan 300 MB akan gagal di perangkat dengan batas 192 MB, tetapi berhasil di flagship dengan 512 MB. Uji pada perangkat dengan spesifikasi minimum untuk menemukan skenario OOM.
largeHeap meningkatkan batas, tetapi tidak mempercepat aplikasi. Jeda GC menjadi lebih lama karena mengumpulkan Heap besar membutuhkan waktu lebih lama. Sistem dapat membunuh aplikasi latar belakang untuk menyediakan memori. Gunakan largeHeap hanya untuk aplikasi yang secara objektif membutuhkan banyak memori (kamera, editor).
OOM — pengecualian di dalam aplikasi saat Heap tidak mencukupi. Pembunuhan oleh sistem (Low Memory Killer) — keputusan kernel Linux untuk membunuh proses guna membebaskan memori untuk aplikasi lain. Dalam pembunuhan oleh sistem, aplikasi tidak menerima pengecualian — proses hanya berakhir.
Rumus: width × height × bytesPerPixel. ARGB_8888 = 4 B/piksel, RGB_565 = 2 B/piksel. Bitmap FullHD (1920 × 1080) dalam ARGB_8888 = 8.3 MB. Bitmap 4K (3840 × 2160) = 33 MB. Selalu skala gambar ke ukuran yang diperlukan untuk ditampilkan di layar.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga