Kebocoran memori — salah satu masalah paling berbahaya dalam pengembangan mobile. Memori aplikasi terus meningkat hingga mencapai batas yang ditetapkan oleh sistem operasi, setelah itu terjadi OutOfMemoryError atau penghentian paksa. Menurut Square Engineering, sekitar 40% aplikasi Android memiliki setidaknya satu kebocoran memori yang hanya dapat dideteksi saat profiling. Mari kita bahas penyebab dan metode pencegahan pertumbuhan memori.
Poin utama
Kebocoran memori (memory leak) — situasi di mana objek yang tidak lagi dibutuhkan aplikasi tetap berada di heap karena ada referensi aktif dari root set (GC Root). Pengumpul sampah menganggap objek tersebut hidup dan tidak menghapusnya.
Pembengkakan memori (memory bloat) — masalah yang lebih luas ketika aplikasi mengonsumsi lebih banyak memori daripada yang diperlukan untuk menjalankan tugas saat ini. Penyebab: caching berlebihan, duplikasi objek, struktur data yang tidak optimal, dan fragmentasi heap.
Di Android setiap aplikasi dialokasikan heap terbatas (biasanya 64-512 MB tergantung perangkat dan versi OS). Di iOS batasannya kurang ketat, tetapi sistem mengirimkan peringatan memori saat mendekati batas.
| Karakteristik | Android | iOS |
|---|---|---|
| Batas heap | 64-512 MB (tergantung perangkat) | Implisit (sistem) |
| Pengumpulan sampah | ART (Concurrent, Compact) | ARC (Automatic Reference Counting) |
| Mekanisme kebocoran | Referensi GC Root | Siklus retain (siklus referensi kuat) |
| Hasil | OutOfMemoryError | Peringatan memori → penghentian |
Menurut Facebook Engineering Blog, kebocoran memori adalah penyebab ~15% laporan crash di aplikasi mobile. Di Android, ditambah ANR karena seringnya jeda GC saat kekurangan memori.
Referensi statis ke Activity — klasik kebocoran Android. Jika bidang statis atau singleton menyimpan referensi ke Activity, Activity tidak akan dikumpulkan oleh GC bahkan setelah finish(), selama singleton masih hidup. Activity adalah objek berat yang berisi hierarki View, resources, dan Context.
object LeakHolder {
var activityRef: Activity ?= null // bocor: referensi statis ke Activity
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle ?= null) {
super.onCreate(savedInstanceState)
LeakHolder.activityRef = this // ❌ MainActivity tidak akan pernah dikumpulkan oleh GC
}
}
Kelas anonim dan lambda — secara implisit menyimpan referensi ke kelas luar. Jika Runnable atau Callback diteruskan ke layanan eksternal dan Activity dihancurkan, objek kelas anonim masih menggantung di antrian dan tidak membiarkan Activity pergi ke pengumpulan sampah.
Di iOS masalah utamanya adalah retain cycles: dua objek saling menyimpan referensi kuat dan ARC tidak bisa menghitung nol untuk keduanya. Kasus tipikal: closure yang menangkap self secara kuat, dan self yang menyimpan referensi ke closure.
LeakCanary — pustaka dari Square untuk deteksi kebocoran otomatis di Android. Setelah Activity atau Fragment dihancurkan, ia memeriksa apakah objek telah dikumpulkan oleh GC. Jika tidak — ia membuat heap dump dan menampilkan trace kebocoran.
// LeakCanary 2.x — integrasi otomatis melalui Application
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
// LeakCanary menginstal otomatis di build debug
// melalui ContentProvider — pengaturan tanpa kode
}
}
// Panggilan pemeriksaan paksa
AppWatcher.objectWatcher.watch(watchedObject, "leak description")
Android Studio Profiler — alat bawaan untuk pemantauan memori secara real-time. Memungkinkan merekam heap dump, menemukan objek mencurigakan (Retained Size > 1 MB), dan melacak jalur GC root ke setiap objek.
Untuk iOS gunakan Xcode Memory Graph Debugger. Ini memvisualisasikan graf objek dalam memori, menampilkan retain cycles dan memungkinkan deteksi instan referensi melingkar. Juga tersedia Instruments > Allocations untuk pemantauan jangka panjang.
WeakReference — mekanisme dasar untuk referensi yang tidak boleh menghalangi pengumpulan sampah. Jika GC memutuskan untuk menghapus objek, WeakReference mengembalikan null. Digunakan untuk callback, listener, dan referensi ke komponen UI dari thread latar belakang.
Komponen Lifecycle-aware — pendekatan arsitektural yang diimplementasikan di Android Jetpack (Lifecycle, LiveData, Flow, coroutines). Langganan secara otomatis dibatalkan saat onDestroy, yang menghilangkan kelas kebocoran utama.
class MyViewModel : ViewModel() {
private val _data = MutableLiveData<List<User>>()
val data: LiveData<List<User>> get() = _data
fun loadData() {
viewModelScope.launch {
val result = repository.fetchData()
_data.postValue(result)
// coroutine otomatis membatalkan saat onCleared()
}
}
}
viewModelScope dan lifecycleScope — CoroutineScope bawaan di Android yang dibatalkan pada peristiwa siklus hidup yang sesuai. Ini menghilangkan kebocoran melalui coroutine — skenario paling umum dalam pengembangan Android modern.
Memory Profiler in Android Studio — alat utama untuk pemantauan heap. Menampilkan alokasi live, snapshot heap, jumlah objek per jenis. Memungkinkan merekam dump dan menganalisisnya di MAT (Memory Analyzer Tool) untuk menemukan objek mencurigakan.
Eclipse MAT — penganalisis heap dump desktop. Setelah memuat file HPROF dari Android Studio, MAT membangun pohon dominator, menampilkan retain size setiap objek, dan menawarkan analisis otomatis kebocoran mencurigakan melalui Leak Suspects Report.
Xcode Memory Graph — debugger visual untuk retain cycles. Saat menekan tombol Memory Graph Debugger, Xcode menghentikan aplikasi, membangun graf lengkap objek dalam memori, dan menyorot retain cycles dengan warna merah.
| Alat | Platform | Fitur |
|---|---|---|
| LeakCanary | Android | Deteksi kebocoran otomatis setelah destroy |
| Memory Profiler | Android Studio | Heap dump + alokasi live |
| Eclipse MAT | Android | Pohon dominator, Leak Suspects Report |
| Memory Graph | iOS (Xcode) | Visualisator retain cycles |
Menurut Google I/O 2023, aplikasi yang menggunakan LeakCanary di build debug mengurangi jumlah crash terkait memori sebesar 30-50% dalam 2 bulan pertama setelah implementasi. Disarankan menambahkan LeakCanary pada fase onboarding proyek.
Pertanyaan yang sering diajukan
Kebocoran — objek yang tidak dapat diakses kode tetapi tidak dihapus GC karena referensi aktif. Pembengkakan — aplikasi menyimpan objek yang secara logis diperlukan tetapi dalam jumlah berlebihan (misalnya, cache 50 MB pada aplikasi yang berjalan seberat 80 MB). Pembengkakan diatasi secara arsitektural, kebocoran — melalui manajemen referensi yang benar.
LeakCanary menggunakan ObjectWatcher — setelah onDestroy() Activity, ia membuat WeakReference ke Activity dan menjalankan GC. Jika setelah 5 detik WeakReference tidak dibersihkan, LeakCanary membuat heap dump, menganalisis rantai referensi terpendek dari GC Root ke objek, dan menampilkan tumpukan kebocoran yang tepat dengan nama file dan baris kode.
Bitmap menggunakan memori di luar heap Java di memori native (native heap). Ukuran satu Bitmap = lebar × tinggi × 4 byte (ARGB_8888). Foto 12 MP (4000×3000) memakan 48 MB. Android tidak selalu dapat membebaskan memori native tepat waktu, yang saat beberapa Bitmap menumpuk menyebabkan OOM bahkan dengan heap Java yang cukup.
Retain cycle — situasi di ARC ketika dua objek saling menyimpan referensi kuat dan penghitung referensi tidak pernah mencapai nol. Contoh tipikal: ViewController dengan referensi kuat ke closure, dan closure menangkap self secara kuat. Solusi: gunakan [weak self] atau [unowned self] di closure.
Ukuran heap tergantung pada perangkat dan versi Android. Untuk perangkat lama (API 15-24) — 64-128 MB. Untuk perangkat modern (API 25+) — 256-512 MB. Nilai pasti dapat diperoleh melalui ActivityManager.getMemoryClass(). Untuk aplikasi besar (game, editor) ada largeHeap=true di manifest, yang memberikan hingga 1 GB.
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