LeakCanary, Square tarafından Android uygulamalarında otomatik bellek sızıntısı tespiti için geliştirilmiş açık kaynaklı bir kütüphanedir. Geliştirme sürecine entegre olur ve Activity, Fragment, ViewModel ve diğer bileşenlerin yaşam döngüsünü gerçek zamanlı olarak izler, sızıntılar oluşur oluşmaz sinyal verir. Square Open Source'a göre kütüphane binlerce projede kullanılmakta ve Android'de bellek teşhisi için fiili standart olarak kabul edilmektedir.
Önemli Noktalar
LeakCanary Square tarafından geliştirilen, Android uygulamalarında otomatik bellek sızıntısı tespiti için bir kütüphanedir. Uygulama derleme sürecine entegre olur ve yok edilmesi gereken nesnelerin (Activity, Fragment, View) bellekte kalıp kalmadığını otomatik olarak izler. Bir sızıntı tespit edildiğinde, LeakCanary bir heap dump oluşturur ve nesneyi tutan referans zincirini analiz eder.
Kütüphane Android topluluğunda bir standart haline gelmiştir: GitHub'a göre proje 28 binden fazla yıldıza sahiptir ve Google, Uber, Airbnb ve Facebook'un uygulamalarında kullanılmaktadır. LeakCanary iki ana sürümde mevcuttur: klasik 1.x (manuel yapılandırma ile) ve modern 2.x (ContentProvider aracılığıyla otomatik entegrasyon). Sürüm 2.x, Application sınıfının değiştirilmesini gerektirmez — tam işlevsellik için bağımlılık yeterlidir.
LeakCanary'nin ana görevi, bir nesnenin yaşam döngüsü sona erdikten sonra bellekte var olmaya devam ettiği durumu tespit etmektir. Bu, statik alanlar, singleton'lar, kaydedilmemiş geri çağırmalar, anonim sınıflar ve harici nesneleri yakalayan kapanışlar yoluyla sızıntılar için tipiktir.
Mobil cihazlardaki sınırlı RAM nedeniyle Android'de bellek sızıntıları masaüstüne göre daha kritiktir. Her ekran geçişinde 5–10 MB'lık bir sızıntı bile 30–40 dakikalık uygulama kullanımından sonra OutOfMemoryError'a yol açabilir. LeakCanary bu tür sorunları geliştirme aşamasında tespit eder, üretimde çökme beklemek zorunda kalmaz.
LeakCanary zayıf referansları (WeakReference) zorunlu çöp toplama ile birleştirerek kullanır. Bir Activity veya Fragment onDestroy çağırdığında, LeakCanary bu nesneye bir WeakReference oluşturur ve kısa bir gecikmeden (varsayılan olarak 5 saniye) sonra GC'yi tetikler. GC'den sonra nesne hala WeakReference aracılığıyla erişilebilirse, güçlü bir referans tarafından tutuluyor demektir — bir sızıntı kaydedilir.
Sızıntı tespit edildikten sonra LeakCanary bir heap dump (bellek dökümü) oluşturur — HPROF formatında uygulama belleğinin tam bir anlık görüntüsü. Ardından yerleşik analizör (sürüm 2.x için Shark) GC Roots'tan sızan nesneye bir erişilebilirlik grafiği oluşturur ve en kısa yolu — nesneyi bellekte tutan referans zincirini bulur.
// LeakCanary tespit mantığı basitleştirilmiş
class ObjectWatcher {
private val watchedReferences = CopyOnWriteArrayList<KeyedWeakReference>()
fun watch(watchedObject: Any, description: String) {
val reference = KeyedWeakReference(watchedObject, description)
watchedReferences.add(reference)
BackgroundHandler.postDelayed({
checkForLeaks()
}, 5000)
}
private fun checkForLeaks() {
GcTrigger.runGc() // zorunlu GC
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // nesne GC'den sağ çıktı — bu bir sızıntı
}
}
}
}
Kilit nokta, GcTrigger.runGc() çağrısının zorunlu olmasıdır. Bu olmadan, gerçekten sızmış bir nesne ile GC'nin henüz toplamadığı bir nesneyi ayırt etmek imkansızdır. LeakCanary bunu üç kez yapar: üç GC döngüsünden sonra nesne hala bellekteyse, sızıntı doğrulanır.
Shark, LeakCanary 2.x'e entegre edilmiş, Kotlin'de yazılmış heap dump analizörüdür. Önceki HAHA analizörünün aksine Shark, HPROF dosyasının tamamını belleğe yüklemez, minimum ayırmalarla nesne grafiğinde gezinir. Bu, analiz sırasında RAM tüketimini 50 MB'tan 2–5 MB'a düşürür ve analiz süresini 30 saniyeden 1–3 saniyeye kısaltır.
Modern bir Android projesine LeakCanary 2.x kurmak build.gradle'da tek satır alır. Kütüphane otomatik başlatma için ContentProvider kullanır — Application sınıfını değiştirmek veya MainActivity'ye kod eklemek gerekmez. Bağımlılık yalnızca debug derlemeleri için eklenir, böylece sürüm APK'ları gereksiz kod içermez.
// build.gradle (app/module)
dependencies {
// debugImplementation — kütüphane yalnızca debug derlemeleri için
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
Bağımlılığı ekleyip projeyi yeniden derledikten sonra LeakCanary otomatik olarak uygulamada görünür. İlk başlatmada kütüphane, etkinleştirmeyi onaylayan bir sistem bildirimi gösterir. Tespit edilen tüm sızıntılar bildirim olarak görünür — bildirime dokunmak ayrıntılı raporu (LeakTrace) içeren bir ekran açar.
Özelleştirme için kendi AppWatcherInstaller'ınızı oluşturabilir ve parametreleri geçersiz kılabilirsiniz: GC zaman aşımı, izlenen nesne türlerinin listesi, heap dump'un diske kaydedilmesini etkinleştirme. Ancak projelerin %90'ı için varsayılan yapılandırma en uygunudur.
Sürüm 2.12'den itibaren LeakCanary, ViewModel, coroutine kapsamları ve Compose State nesnelerinin otomatik izlenmesini destekler. Ek bağımlılık gerekmez — kütüphane projede hangi Jetpack bileşenlerinin kullanıldığını otomatik olarak algılar ve ilgili dedektörleri etkinleştirir.
LeakCanary raporu (LeakTrace), GC Root'tan sızan nesneye kadar çok satırlı bir referans zinciridir. Her satır, güçlü bir referansın geçtiği sınıf ve alanı gösterir. Geliştirici zinciri aşağıdan yukarıya okumalıdır: alt satır sızan nesne, üst satır giriş noktasıdır (GC Root).
Tipik bir LeakTrace şöyle görünür: GC Root → Application statik alanı → singleton → geri çağırma → Activity. Bir geliştirici böyle bir zincir görürse sorun açıktır: singleton, Activity'ye bir referans yakalamış bir geri çağırmayı tutuyor. Çözüm, singleton'daki güçlü referansı zayıf referansla değiştirmektir.
┬
├─ android.app.Application
│ Leaking: NO (Application — singleton)
│ ↓ Application.leakedActivities
├─ java.util.ArrayList
│ Leaking: NO (ArrayList — normal)
│ ↓ ArrayList[0]
├─ com.example.MainActivity
│ Leaking: YES (Activity destroyed but still in memory)
│ ↓ MainActivity.mCallback
├─ com.example.CallbackWrapper
│ Leaking: UNKNOWN
│ ↓ CallbackWrapper.mListener
│ ~~~~~~~~~~
├─ com.example.MyCallback (anonymous)
│ Leaking: UNKNOWN
│ ↓ MyCallback.this$0
├─ com.example.MainActivity
│ Leaking: YES (MainActivity is the leak)
╰
Bu örnekte LeakCanary, MainActivity'nin zincir yoluyla tutulduğunu gösteriyor: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → tekrar MainActivity. this$0 oku, anonim sınıf MyCallback'in Activity'ye harici bir referans yakaladığını gösterir. Çözüm, geri çağırmayı zayıf referans yapmak veya onDestroy'de iptal etmektir.
LeakCanary ayrıca zincirdeki her öğe için sızıntı durumunu gösterir: NO (sızıntı yok — kök öğe), YES (nesne yok edilmeli), UNKNOWN (durum belirlenemedi). UNKNOWN durumu bir sorun anlamına gelmez — LeakCanary'nin kesin olarak sınıflandıramadığı bir ara nesnedir.
Sürüm 1.x'ten 2.x'e geçiş köklüydü: geliştiriciler kütüphaneyi sıfırdan yeniden yazdı, eski HAHA analizörünü Kotlin'de yazılmış kendi motorları Shark ile değiştirdi. Shark çok daha hızlıdır, analiz için daha az bellek gerektirir ve sızıntıların temel nedenlerini daha doğru belirler.
| Parametre | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| Analizör dili | Java (HAHA — Android SDK fork'u) | Kotlin (Shark — kendi motoru) |
| Kurulum | Application'da manuel AppWatcher yapılandırması | ContentProvider aracılığıyla otomatik |
| Hız | Heap dump analizi için 10–30 saniye | Heap dump analizi için 1–5 saniye |
| Performans | Analiz sırasında 10–50 MB RAM tüketir | Analiz sırasında 2–10 MB RAM tüketir |
Shark'ın temel avantajı, heap dump'ın tamamını belleğe yüklememesi, minimum ayırmalarla referans grafiğinde gezinmesidir. Bu, LeakCanary 2.x'i analiz sırasında OutOfMemoryError riski olmadan düşük RAM'li cihazlarda kullanıma uygun hale getirir.
Sürüm 2.x ayrıca Android Studio Memory Profiler'da daha sonra analiz için heap dump'ları dosyaya dışa aktarma özelliğini de getirmiştir. Bunu yapmak için AppWatcher yapılandırmasında dumpHeapWhenLeakFound ayarını etkinleştirin.
LeakCanary, Android'de yaygın olan birkaç sızıntı sınıfını etkili bir şekilde tespit eder. En sık görüleni, Activity'ye statik referanslar yoluyla sızıntıdır — geliştiriciler singleton'da Activity bağlamına bir referans tutar ve Activity, yaşam döngüsü sona erdikten sonra GC tarafından toplanamaz.
İkinci en yaygın kategori, kaydedilmemiş dinleyiciler yoluyla sızıntılardır. onStart'da registerListener çağrılmış ancak onStop/onDestroy'da unregisterListener çağrılmamışsa, dinleyici nesnesi aktivite yok edildikten sonra bile sistem tarafından tutulur. LeakCanary hangi dinleyicinin ve hangi sistem hizmetinde canlı kaldığını açıkça gösterir.
// Tipik sızıntı: Singleton geri çağırmasında yakalanan Activity
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // geri çağırmaya güçlü referans
}
fun unregister() {
callback = null // onDestroy'de çağırmayı UNUTMAYIN!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // lambda this'i yakalar
}
// onDestroy'de unregister çağrılmazsa → Activity sızıntısı
}
}
Üçüncü kategori, BackStack'teki Fragment yoluyla sızıntılardır. FragmentTransaction.addToBackStack() geri dönerken Fragment kaldırılmadan çağrılırsa, eski Fragment örnekleri bellekte kalır. LeakCanary, geliştirmenin erken aşamalarında bu tür gizli sızıntıları tespit etmeye yardımcı olur.
Tespit edilen her sızıntı için LeakCanary bir açıklama ve düzeltme önerileri sağlar. Sürüm 2.14, Android Lint ile entegrasyon eklemiştir — kütüphane CI'da bir sızıntı tespit edildiğinde otomatik olarak sorun izleyicide görev oluşturabilir.
Sıkça Sorulan Sorular
Evet, kesinlikle. LeakCanary build.gradle'da debugImplementation aracılığıyla eklenir ve bu da onu otomatik olarak sürüm derlemelerinden hariç tutar. implementation kullanılırsa kütüphane sürüm APK'sına dahil olur ve son kullanıcılara sızıntıları gösterir — bu kabul edilemez.
Performans etkisi minimumdur. LeakCanary yalnızca bileşenin onDestroy'sinden sonra etkinleşir ve UI oluşturma veya dokunma işleme ile etkileşime girmez. Tek maliyet, kısa bir zorunlu GC duraklaması (yaklaşık 100 ms) ve sızıntı oluştuğunda heap dump yazmadır (saniyenin kesirleri).
LeakCanary otomatik olarak heap dump'ları HPROF formatında uygulama klasörüne kaydeder. Dosya Android Studio aracılığıyla dışa aktarılabilir: Device File Explorer → data/data/com.example/files/leakcanary/. Görüntülemek için Capture → Open Heap Dump yoluyla Memory Profiler'da dosyayı açın.
Evet, sürüm 2.12'den itibaren LeakCanary Jetpack Compose'u tam olarak destekler. Kütüphane Composition bağlamlarını ve State nesnelerini izler, Composable işlevlerde otomatik olarak sızıntıları tespit eder. Ayrı bir yapılandırma gerekmez — kutudan çıktığı gibi çalışır.
Yanlış pozitifler mümkün ancak nadirdir. LeakCanary, sızıntı bildirimi yapmadan önce üçlü bir GC çağrısı kullanır ve bu da çoğu yanlış pozitifi ortadan kaldırır. Bir tespitin yanlış pozitif olduğunu düşünüyorsanız, yapılandırmada belirli sınıf için bir IgnoredReference oluşturun.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun