Kebocoran memori (Memory Leak) — situasi ketika aplikasi menyimpan referensi ke objek yang tidak lagi diperlukan, mencegah garbage collector membebaskan occupied memory. Menurut data LeakCanary, bahkan di aplikasi yang ditulis dengan baik terdapat 3–5 kebocoran per 10.000 baris kode. Setiap kebocoran secara bertahap mengurangi memori yang tersedia, menyebabkan perlambatan dan OutOfMemoryError.
Poin Utama
Kebocoran memori (Memory Leak) — situasi di mana suatu objek tetap dapat dijangkau melalui rantai referensi kuat (Strong Reference), meskipun secara logis sudah tidak diperlukan oleh aplikasi. Garbage collector (GC) menganggap objek tersebut hidup dan tidak membebaskan memori yang ditempatinya. Akibatnya, memori heap yang tersedia terus berkurang dan frekuensi jeda GC meningkat.
Berbeda dengan bahasa dengan manajemen memori manual (C, C++), di Java/Kotlin kebocoran bukanlah free() yang terlupakan, melainkan referensi yang terlupakan. Selama ada strong reference dari objek akar (GC Root) ke objek yang bocor, GC menganggapnya diperlukan. GC Root tipikal: field statis, thread aktif, stack panggilan, referensi global JNI.
Bahaya kebocoran adalah efek kumulatifnya. Satu kebocoran 100 KB tidak terlihat, tetapi 100 kebocoran semacam itu memakan 10 MB dan aplikasi mulai melambat karena GC yang sering. Massa kritis kebocoran menyebabkan OutOfMemoryError dan crash aplikasi. Gejala kebocoran: peningkatan konsumsi memori yang konstan pada grafik Profiler, jeda GC yang sering dengan STW (Stop The World) dan penurunan kinerja UI.
Lima jenis kebocoran mencakup 95% kasus dalam pengembangan mobile. Masing-masing memiliki penyebab dan pola karakteristik dalam kode.
Kebocoran paling terkenal di Android — menyimpan referensi statis ke Activity atau Context. Kode tipikal: field statis Activity yang tidak di-nolkan saat onDestroy(). Selama field statis hidup, seluruh Activity dengan pohon View-nya yang bisa memakan 1–10 MB tetap hidup. Ini adalah kebocoran klasik yang LeakCanary temukan pertama kali.
Solusi: jangan pernah menyimpan Activity atau Context di field statis. Gunakan Application Context untuk singleton yang hidup lebih lama dari Activity. Jika perlu referensi ke Activity — gunakan WeakReference<Activity>.
object MySingleton {
private var weakActivity: WeakReference<Activity>? = null
fun attach(activity: Activity) {
weakActivity = WeakReference(activity)
}
}
Kelas anonim dan kelas bersarang non-statis secara implisit menyimpan referensi ke kelas yang mengandungnya. Runnable yang dikirim ke Handler dan dieksekusi setelah onDestroy() menahan seluruh Activity. Callback Retrofit yang menutup Activity melakukan hal yang sama. Ini adalah jenis kebocoran paling berbahaya — referensi implisit tidak terlihat dalam kode.
Ekspresi object dan lambda Kotlin juga menangkap referensi ke kelas eksternal. Buat kelas bersarang menjadi statis (atau top-level di Kotlin) dan teruskan referensi eksternal melalui WeakReference. Untuk lambda, gunakan pendekatan Lifecycle-aware dengan viewLifecycleOwner.
Berlangganan ke layanan sistem tanpa pembatalan — kebocoran langsung. SensorManager, LocationManager, NotificationListener yang didaftarkan di onResume() tanpa memanggil unregister di onPause() menahan Activity. Serupa: RxJava Disposable yang tidak ditambahkan ke CompositeDisposable dan coroutine yang dijalankan melalui GlobalScope.
Gunakan komponen Lifecycle-aware: observe() dengan LifecycleOwner secara otomatis berhenti berlangganan saat onDestroy(). Untuk RxJava — viewLifecycleOwner.lifecycle.addObserver dengan DisposableObserver. Untuk coroutine — lifecycleScope.launch() terikat pada siklus hidup.
// pembatalan langganan otomatis melalui Lifecycle
viewModel.userData.observe(viewLifecycleOwner) { data ->
updateUI(data)
}
// coroutine dengan lifecycleScope
lifecycleScope.launch {
viewModel.loadData().collect { render(it) }
}
Bitmap memakan jumlah memori heap yang signifikan: satu FullHD-bitmap — 1920 × 1080 × 4 byte = 8.3 MB. Jika Bitmap dibuat untuk setiap elemen daftar dan recycle() tidak dipanggil saat disembunyikan, memori cepat habis. Pada versi Android lama (sebelum 3.0), Bitmap disimpan di memori native, tetapi pada versi modern — di heap Dalvik/ART, dan GC hanya dapat membebaskannya jika tidak ada strong reference.
Gunakan Glide atau Coil untuk memuat gambar — pustaka ini mengelola caching dan recycle secara otomatis. Jika bekerja langsung dengan Bitmap, panggil bitmap.recycle() untuk gambar besar yang tidak lagi ditampilkan dan gunakan inSampleSize untuk memuat saluran yang diperkecil.
Fragment memiliki dua siklus hidup: Fragment itu sendiri dan View-nya. Setelah onDestroyView() pohon View dihancurkan, tetapi Fragment itu sendiri dapat tetap di memori jika ada referensi eksternal. Kesalahan tipikal — menyimpan referensi ke Fragment di adapter ViewPager atau di grafik navigasi yang tidak dibersihkan saat dihancurkan.
Jangan pernah menyimpan referensi ke Fragment di field objek berumur panjang. Gunakan childFragmentManager untuk fragment bersarang dan observe() dengan LifecycleOwner untuk mentransfer data di antara mereka. ViewPager2 telah memecahkan masalah ini di tingkat API: FragmentTransactionAdapter mengelola siklus hidup dengan benar.
Deteksi kebocoran memerlukan pemeriksaan dua fakta: memori tidak kembali setelah expected lifetime dan jumlah objek dari tipe tertentu meningkat tanpa penurunan. Proses diagnostik mencakup tiga tahap.
Tahap pertama — pemeriksaan visual melalui Memory Profiler di Android Studio. Buka tab Memory, lakukan tindakan target (buka dan tutup layar), tekan GC (Garbage Collection) dan lihat apakah memori kembali ke tingkat awal. Jika setelah 3–4 siklus buka-tutup memori terus meningkat — ada kebocoran.
Tahap kedua — mengambil Heap Dump. Di Memory Profiler tekan Dump Java Heap. File .hprof yang diperoleh buka di Android Studio: Anda akan melihat semua objek di heap dengan ukuran dan referensi. Cari kelas yang jumlahnya harus nol setelah layar ditutup. Misalnya, MainActivity dengan jumlah 2 setelah ditutup — kebocoran yang jelas.
Tahap ketiga — analisis Retained Size dan GC Root. Di Android Studio analisis Retained Size: berapa banyak memori yang akan dibebaskan jika Anda menghapus objek ini. Jalur dari GC Root ke objek menunjukkan apa yang menahannya: Static field → HashMap → Activity — dan Anda melihat titik kebocoran. Panel Reference widget menunjukkan semua pemegang objek.
Empat alat mencakup pencarian kebocoran dari deteksi otomatis hingga analisis Heap Dump mendalam.
| Alat | Metode | Format Hasil |
|---|---|---|
| LeakCanary | Pemantauan otomatis | Heap Dump + stack trace kebocoran |
| Android Memory Profiler | Pemantauan manual | Grafik memori + Heap Dump |
| MAT (Eclipse) | Analisis mendalam | Laporan Dominator Tree + jalur GC Root |
| Perfetto | Pelacakan system-wide | Garis waktu + memori native |
LeakCanary — must-have untuk setiap proyek Android. Secara otomatis mendeteksi kebocoran setelah siklus hidup Activity/Fragment berakhir dan menunjukkan lokasi tepat kebocoran dengan stack trace. Integrasi: satu baris di build.gradle. LeakCanary 2.x tidak memerlukan inisialisasi manual — secara otomatis mendaftarkan Application Watcher.
Pencegahan kebocoran dibangun ke dalam proses pengembangan melalui seperangkat aturan dan alat yang memeriksa kode di setiap tahap.
Jangan pernah menyimpan referensi ke Activity, Fragment atau View di field statis, singleton atau objek berumur panjang. Jika referensi tidak dapat dihindari — gunakan WeakReference atau simpan data melalui ViewModel yang hidup persis selama yang diperlukan dan tidak menyimpan View secara langsung.
ViewModel dan LiveData dari Android Architecture Components memecahkan masalah siklus hidup di tingkat arsitektur. ViewModel bertahan dari rotasi layar dan tidak mengandung referensi ke View. LiveData secara otomatis berhenti berlangganan observer saat onDestroy(). Gunakan mereka daripada berlangganan manual ke layanan sistem.
Pada code review perhatikan: field statis dengan tipe Context/View, kelas anonim, lambda yang menutup Activity, langganan manual, RxJava disposable tanpa composite, penyimpanan Fragment melalui Bundle. Di Kotlin, periksa juga coroutine untuk launch tanpa ikatan ke siklus hidup.
LeakCanary dapat berfungsi sebagai bagian dari pipeline pengujian: jalankan pengujian penerimaan dengan LeakCanary dan tandai build gagal jika ditemukan kebocoran. Ini mencegah kebocoran masuk ke produksi. Lengkapi pemeriksaan dengan Android Lint dengan aturan StaticFieldLeak — aturan ini menemukan potensi kebocoran di tingkat analisis statis.
// LeakCanary dalam pengujian
class LeakTest {
@Test
fun activityShouldNotLeak() {
ActivityScenario.launch(MainActivity::class.java)
.close()
LeakAssertions.assertNoLeak() // fail jika ada kebocoran
}
}
Pertanyaan yang Sering Diajukan
Kebocoran adalah penyebab, dan OutOfMemoryError adalah akibat. Satu kebocoran tidak menyebabkan OOM, tetapi akumulasi puluhan kebocoran menghabiskan Heap. OOM adalah pengecualian fatal, dan kebocoran adalah pola yang menyebabkan OOM seiring waktu.
Melalui Android Memory Profiler: buka dan tutup layar 5 kali, setelah setiap penutupan panggil GC. Jika memori tidak kembali ke tingkat dasar — ada kebocoran. Ambil Heap Dump dan temukan di daftar kelas Activity yang jumlahnya lebih dari 0 setelah penutupan.
Sebagian. Kotlin memecahkan masalah null-safety, tetapi tidak mengelola strong references. Coroutine dengan lifecycleScope dan viewModelScope mencegah kebocoran dari tugas latar belakang, dan sealed class serta data class mengurangi jumlah kondisi yang menyebabkan kebocoran. Perlindungan utama — pola arsitektur, bukan fungsi bahasa.
LeakCanary terkadang memberikan false positive: suatu objek mungkin ditahan sementara oleh sistem (misalnya, InputMethodManager menahan View terakhir). Periksa secara manual: jika Retained Size < 1 KB dan GC Root adalah layanan sistem, kemungkinan itu adalah alarm palsu.
Tidak. Kebocoran mungkin terjadi di platform mana pun dengan GC: iOS (Swift/Objective-C), Flutter (Dart), peramban web (JavaScript). Mekanismenya sama — strong reference dari GC Root. Di iOS, ARC mengelola memori secara otomatis, tetapi retain cycle antar objek menciptakan kebocoran yang sama.
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