Android’de LeakCanary Kullanarak Memory Leak Tespiti
Herkese selamlar. Bu yazıda Android uygulamalar yazarken istemesek de karşılaştığımız memory leakleri LeakCanary kütüphanesi yardımıyla nasıl tespit edebilirizi anlatmaya çalışacağım.
LeakCanary, Android uygulamalarındaki memory leakleri bulmak için kullanabileceğimiz kütüphanelerden birisidir. Herhangi bir kod yazmamıza gerek olmadığı için kullanımı da oldukça kolaydır.
LeakCanary sayesinde ANR(Application Not Responding) olarak bildiğimiz donmaların/takılmaların ve OutOfMemoryError hatalarına sebep olan leaklerin kök nedenlerini bulabiliriz.
LeakCanary’yi projemize nasıl ekleriz ve nasıl kullanırıza geçmeden önce memory leak nedir kısaca ondan bashedelim.
Memory leak, bir uygulamanın artık ihtiyaç duyulmayan, kullanımı biten bir nesneye ait referansı tutmasına neden olan bir programlama hatasıdır. Sonucunda, o nesne için ayrılan memory geri alınamaz, garbage collector tarafından temizlenemez ve OutOfMemoryError (OOM) hatasına yol açar.
Örnek olarak, Androidde bir activity instance’ına, o activitynin onDestroy metodu çağrıldıktan sonra ihtiyaç duymayız. Ama bu instance’ın referansını static bir alanda tutuyorsak, garbage collector bu alanı temizleyemez ve bu durum leak’e sebep olur.
LeakCanary nasıl çalışır? Bunu kısaca anlatmaya çalışacağım çünkü nasıl çalıştığından ziyade nasıl kullanırız bizim için daha önemli.
LeakCanary, Android yaşam döngüsüne derinlemesine dalarak Activity ve Fragment’lerin ne zaman destroy edildiklerini ve garbage collector tarafından temizlenip temizlenmemeleri gerektiğini tespit eder.
Eğer temizlenmesi gerektiği tespit edilen bu instancelar 5 saniye (default) içinde temizlenmezse LeakCanary bunu bir leak olarak kabul eder ve loglar.
Bu leakler bir threshold değerine ulaştığında (default 5), LeakCanary heap’i analiz ederek leak’e sebep olan durumların detaylı analizini -leak trace- çıkarır. Bunu hem Logcatte hem de bildirimlerde görebiliriz.
Çalışma şeklinin detaylı anlatımını şuraya bırakıyorum: LeakCanary
Peki LeakCanary’yi nasıl kullanırız?
Yapmamız gereken sadece gradle’a kütüphanenin dependency’sini eklemek.
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
debugImplementation olarak ekliyoruz çünkü bu kütüphane sadece debug buildlerde çalışacak, release buildlerde çalışmamış olacak.
Logcat’e tag:LeakCanary filtresini ekleyerek LeakCanary loglarını bir arada derli toplu şekilde görebiliriz. Logcati farklı tagler için de (Network vs) bu şekilde filtrelerle özelleştirip ayrı sekmelerde kullanmanızı öneririm.
Örnek olarak MainActivity’de static bir alanda ikinci bir activity’nin referansını tuttuğumuz, leak’e sebep olabilecek dummy bir proje düşünelim.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Intent(this, SecondActivity::class.java).also {
startActivity(it)
}
}
companion object {
lateinit var context: Context
}
}
class SecondActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
MainActivity.context = this
}
}
MainActiviy oluşur oluşmaz SecondActivity açılacak ve MainActivity’de static olarak tanımladığımız context’e Second activity referansını atayacağız. Daha sonrasında geri gelip SecondActivity’yi kill etsek bile context’i static olarak tuttuğumuz için MainActivity’deki SecondActivity referansı temizlenemeyecek ve leak’e sebep olmuş olacak.
Logcat’e baktığımızda yakalanan leakler ve detaylı analizlerini görebiliriz. Tam olarak üstte yazdığım gibi context referansından dolayı leak olmuş.
Aynı analizi şu şekilde test cihazımızda da görüntüleyebiliriz.
Kodu en baştan temiz yazıp buralara hiç düşmemek en iyisi tabi ama günün sonunda böyle kütüphanelere/toollara ihtiyacımız oluyor :) Bundan sonra daha az ihtiyacığımız olması dileğiyle, herkese keyifli kodlamalar.