Kebocoran Memori dan Perbaikannya
Temukan dan hentikan kebocoran.
Kebocoran Memori dan Perbaikannya adalah pelajaran Android Academy gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar Android Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus Android Academy mencakup 4 pelajaran total.
Apa Itu Kebocoran Memori?
Kebocoran memori terjadi ketika objek yang tidak lagi diperlukan tidak dapat dikumpulkan oleh pengumpul sampah karena sesuatu masih menyimpan referensi ke objek tersebut. Di Android, korban klasiknya adalah Activity atau Context yang tetap ada setelah layarnya dihancurkan.
Kebocoran memperbesar heap seiring waktu, memicu pengumpulan sampah yang lebih sering (yang menjeda UI dan menyebabkan jank), dan akhirnya membuat aplikasi mogok dengan OutOfMemoryError. Pelajaran ini menunjukkan cara menemukan dan memperbaikinya.
Cara GC Menentukan yang Harus Dipertahankan
Pengumpul sampah Android mempertahankan objek apa pun yang dapat dijangkau dari akar GC (thread yang aktif, kolom statis, dan sebagainya). Semua yang tidak dapat dijangkau akan dibebaskan.
Kebocoran hanyalah jalur keterjangkauan yang tidak diinginkan: objek berumur panjang yang menyimpan referensi ke objek berumur pendek. Demo Kotlin murni di bawah menunjukkan bagaimana keterjangkauan membuat objek tetap hidup.
object GlobalCache {
val items = mutableListOf<ByteArray>()
}
fun cacheSomething() {
// This 1MB array stays alive forever because GlobalCache
// (a static singleton, a GC root) keeps referencing it.
GlobalCache.items.add(ByteArray(1_000_000))
}
fun main() {
repeat(3) { cacheSomething() }
println("Held arrays: ${GlobalCache.items.size}") // never freed
}Klasik: Aktivitas yang Bocor
Kebocoran Android yang paling umum terjadi ketika objek statis atau berumur panjang menyimpan Context. Saat Aktivitas dihancurkan (karena rotasi atau navigasi), sistem ingin membebaskannya, tetapi referensi statis membuatnya tetap hidup selamanya.
Kode di bawah ini menyebabkan kebocoran: singleton menyimpan Context Aktivitas.
// LEAK: static field holds an Activity Context
object Analytics {
var context: Context? = null // <- keeps Activity alive
fun init(ctx: Context) { context = ctx }
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Analytics.init(this) // passing the Activity -> leak on rotation
}
}Perbaikan: Gunakan Context Aplikasi
Jika Anda harus menyimpan Context di dalam sesuatu yang berumur panjang, simpanlah context aplikasi, yang hidup selama seluruh proses dan aman untuk dipertahankan.
Simpan Context Aktivitas/Tampilan hanya selama layar tersebut masih ada. Aturan sederhana ini mencegah sebagian besar kebocoran Android.
object Analytics {
private var appContext: Context? = null
fun init(ctx: Context) {
// applicationContext is process-scoped and safe to keep
appContext = ctx.applicationContext
}
}
// Call site is now leak-free:
Analytics.init(this) // stores applicationContext, not the ActivityKelas Dalam dan Handler
Kelas dalam nonstatis (termasuk sebagian besar listener, Runnable, dan callback Handler) menyimpan referensi implisit ke kelas luarnya. Jika pekerjaan tersebut berlangsung lebih lama daripada layar, layar itu akan bocor.
Handler.postDelayed yang tertunda sering menjadi penyebabnya: pesan yang masih menunggu membuat Aktivitas tetap hidup sampai pesan tersebut dijalankan.
// LEAK: the posted Runnable holds the Activity for 60 seconds
handler.postDelayed({ updateUi() }, 60_000)
// FIX: cancel pending work when the screen goes away
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacksAndMessages(null)
}Korutin: Batasi Ruang Lingkup Pekerjaan
Korutin yang hidup lebih lama daripada layarnya akan membocorkan semua hal yang ditangkapnya. Perbaikannya adalah konkurensi terstruktur: jalankan pekerjaan dalam ruang lingkup yang terikat pada siklus hidup agar pekerjaan tersebut dibatalkan secara otomatis.
Di dalam ViewModel, gunakan viewModelScope; di dalam pengendali UI, gunakan lifecycleScope. Saat pemiliknya dihancurkan, ruang lingkup tersebut membatalkan pekerjaan dan referensi dilepaskan.
class FeedViewModel : ViewModel() {
fun load() {
// Cancelled automatically when the ViewModel is cleared
viewModelScope.launch {
val feed = repository.fetchFeed()
_state.value = feed
}
}
}
// In Compose, collect tied to the lifecycle:
val state by viewModel.state.collectAsStateWithLifecycle()DisposableEffect di Compose
Di Compose, apa pun yang Anda daftarkan harus dibatalkan pendaftarannya saat komponen yang dapat disusun keluar. DisposableEffect menyediakan blok onDispose khusus untuk itu: listener, pengamat, sensor, dan penerima siaran.
Jika pembersihan di sini terlupa, callback dan semua hal yang ditangkapnya akan bocor.
@Composable
fun LocationDisplay(manager: LocationManager) {
DisposableEffect(manager) {
val listener = LocationListener { /* update */ }
manager.register(listener)
onDispose { manager.unregister(listener) } // prevents the leak
}
}Menemukan Kebocoran dengan LeakCanary
LeakCanary adalah alat standar untuk menemukan kebocoran secara otomatis pada build debug. Tambahkan dependensinya, lalu alat ini akan mengawasi objek yang dihancurkan; jika suatu objek tidak dikumpulkan oleh pengumpul sampah, alat ini membuang isi heap dan menampilkan rantai referensi persis yang membuatnya tetap hidup.
Rantai tersebut menjadi petunjuk perbaikannya: rantai itu langsung mengarah ke field atau listener yang bermasalah.
// build.gradle.kts (app module)
dependencies {
debugImplementation("com.squareup.leakcanary:leakcanary-android:2.14")
}
// No code needed: on a debug build, navigate away from a screen and
// LeakCanary posts a notification with the leak trace, e.g.:
// Analytics.context -> MainActivity (leaked)Heap Dump di Profiler
Untuk kebocoran yang tidak ditandai oleh LeakCanary (pertumbuhan lambat, kebocoran native, atau cache besar), gunakan Memory Profiler. Ambil heap dump, paksa GC, lalu periksa kelas mana yang memiliki jumlah instans yang tidak wajar.
Jumlah instans Aktivitas, Fragmen, atau ViewModel Anda yang terus bertambah setelah berpindah dari layar tersebut adalah tanda kuat adanya kebocoran. Profiler juga menampilkan jalur menuju akar GC sehingga Anda dapat melacak referensinya.
Bitmap dan Alokasi Besar
Tidak semua masalah memori merupakan kebocoran referensi. Bitmap besar dan cache tanpa batas dapat memenuhi heap dengan sendirinya. Foto beresolusi penuh dapat menggunakan memori hingga puluhan megabita.
Biarkan pustaka gambar (Coil/Glide) menurunkan resolusi gambar sesuai ukuran tampilannya, dan beri batas eksplisit pada setiap cache yang Anda buat. Potongan kode tersebut menunjukkan cache LRU dengan batas ukuran.
// Bound memory: keep at most ~1/8 of available app memory
val maxKb = (Runtime.getRuntime().maxMemory() / 1024 / 8).toInt()
val bitmapCache = object : LruCache<String, Bitmap>(maxKb) {
override fun sizeOf(key: String, value: Bitmap) = value.byteCount / 1024
}
// An unbounded HashMap<String, Bitmap> would grow until OutOfMemoryError.Daftar Periksa Pencegahan Kebocoran
Biasakan hal-hal berikut agar sebagian besar kebocoran tidak pernah terjadi:
- Simpan applicationContext di objek berumur panjang, jangan pernah menyimpan Aktivitas/Tampilan.
- Jalankan pekerjaan asinkron dalam korutin yang ruang lingkupnya mengikuti siklus hidup (
viewModelScope,lifecycleScope). - Selalu batalkan pendaftaran listener/penerima (
onDispose,onDestroy). - Batalkan Handler yang tertunda dan pewaktu.
- Beri batas pada cache dan turunkan resolusi bitmap.
- Sertakan LeakCanary dalam build debug dan tindak lanjuti jejaknya.
Pemeriksaan Singkat
Anda memerlukan objek singleton untuk menyimpan Context selama seluruh masa hidupnya. Context mana yang harus disimpan agar layar tidak bocor?
Ringkasan: Jaga Kebersihan Memori
Anda telah mempelajari apa itu kebocoran dan cara menghentikannya:
- Kebocoran adalah jalur keterjangkauan yang tidak diinginkan dan membuat objek yang seharusnya sudah mati tetap hidup.
- Kebocoran klasik adalah referensi statis/berumur panjang ke Aktivitas/Context — gunakan applicationContext sebagai gantinya.
- Listener dalam kelas, Handler yang tertunda, dan korutin tanpa ruang lingkup membocorkan pemiliknya — batasi ruang lingkup dan batalkan semuanya.
- Gunakan
DisposableEffectdi Compose untuk membatalkan pendaftaran callback. - LeakCanary dan Memory Profiler mengungkap rantai referensi.
- Beri batas pada cache dan turunkan resolusi bitmap.
Berikutnya: membuat aplikasi memulai dengan cepat melalui pengoptimalan startup dan profil dasar.
Pertanyaan yang Sering Diajukan
Apakah pelajaran “Kebocoran Memori dan Perbaikannya” gratis?
Ya — teks lengkap “Kebocoran Memori dan Perbaikannya” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus Android Academy, upgrade ke CoddyKit PRO. Kursus Android Academy mencakup 4 pelajaran total.
Apa yang akan aku pelajari di “Kebocoran Memori dan Perbaikannya”?
Temukan dan hentikan kebocoran. Kamu berlatih Android Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.
Apakah aku perlu pengalaman untuk memulai Android Academy?
Tidak diperlukan pengalaman sebelumnya. Android Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.
Berapa lama pelajaran “Kebocoran Memori dan Perbaikannya” memakan waktu?
Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.
Bisakah aku menulis dan menjalankan kode dalam pelajaran Android Academy ini?
Ya. Setiap pelajaran Android Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.
Semua pelajaran dalam kursus ini
- Mengukur Kinerja
- Menjinakkan Rekomposisi
- Kebocoran Memori dan Perbaikannya
- Profil Startup dan Baseline