LeakCanary — is een open source bibliotheek van Square voor automatische detectie van geheugenlekken in Android-applicaties. Het integreert in het ontwikkelproces en volgt in realtime de levenscyclus van Activity, Fragment, ViewModel en andere componenten, waarbij het lekken direct na hun optreden signaleert. Volgens gegevens van Square Open Source, wordt de bibliotheek gebruikt in duizenden projecten en wordt het beschouwd als de de facto standaard voor geheugendiagnostiek op Android.
Belangrijkste punten
LeakCanary — is een bibliotheek voor automatische detectie van geheugenlekken in Android-applicaties, ontwikkeld door Square. Het wordt ingebed in het buildproces van de applicatie en volgt automatisch wanneer objecten die vernietigd zouden moeten worden (Activity, Fragment, View) in het geheugen blijven. Bij detectie van een lek maakt LeakCanary een heap dump en analyseert de keten van referenties die het object vasthoudt.
De bibliotheek is een standaard geworden in de Android-gemeenschap: volgens GitHub heeft het project meer dan 28.000 sterren verzameld en wordt het gebruikt in applicaties van Google, Uber, Airbnb en Facebook. LeakCanary is beschikbaar in twee hoofdversies: klassiek 1.x (met handmatige configuratie) en modern 2.x (automatische integratie via ContentProvider). Versie 2.x vereist geen wijziging van de Application-klasse — één afhankelijkheid is voldoende voor volledige werking.
De belangrijkste taak van LeakCanary is het detecteren van situaties waarin een object in het geheugen blijft bestaan nadat zijn levenscyclus is voltooid. Dit is typisch voor lekken via statische velden, singletons, niet-geregistreerde callbacks, anonieme klassen en closures die externe objecten vastleggen.
Geheugenlekken op Android zijn kritieker dan op desktop vanwege de beperkte hoeveelheid RAM op mobiele apparaten. Zelfs een lek van 5–10 MB bij elke schermovergang kan leiden tot OutOfMemoryError na 30–40 minuten gebruik van de applicatie. LeakCanary detecteert dergelijke problemen in de ontwikkelingsfase, zonder te wachten op een crash in productie.
LeakCanary gebruikt zwakke referenties (WeakReference) in combinatie met geforceerde aanroep van de garbage collector. Wanneer Activity of Fragment onDestroy aanroept, maakt LeakCanary een WeakReference naar dit object en start GC na een korte vertraging (standaard 5 seconden). Als het object na GC nog steeds toegankelijk is via WeakReference, betekent dit dat het wordt vastgehouden door een sterke referentie — er wordt een lek geregistreerd.
Na detectie van een lek maakt LeakCanary een heap dump (volledige momentopname van het applicatiegeheugen in HPROF-formaat). Vervolgens bouwt de ingebouwde analyzer (Shark voor versie 2.x) een bereikbaarheidsgraf van GC Roots naar het gelekte object en vindt het kortste pad — de keten van referenties die het object in het geheugen vasthoudt.
// Vereenvoudigde detectielogica van LeakCanary
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() // geforceerde GC
for (ref in watchedReferences) {
if (ref.get() != null) {
onLeakFound(ref) // object heeft GC overleefd — dit is een lek
}
}
}
}
Het belangrijkste punt — de geforceerde aanroep van GcTrigger.runGc(). Zonder dit is het onmogelijk om een werkelijk gelekt object te onderscheiden van een object dat GC nog niet heeft kunnen verzamelen. LeakCanary doet dit tot drie keer: als na drie GC-cycli het object nog steeds in het geheugen is — wordt het lek bevestigd.
Shark — is de ingebouwde heap dump analyzer in LeakCanary 2.x, geschreven in Kotlin. In tegenstelling tot de vorige analyzer HAHA, laadt Shark niet het hele HPROF-bestand in het geheugen, maar doorloopt het de objectengraf met minimale allocaties. Dit vermindert het RAM-verbruik tijdens analyse van 50 MB naar 2–5 MB en verkort de analysetijd van 30 seconden naar 1–3 seconden.
Installatie van LeakCanary 2.x in een modern Android-project duurt één regel in build.gradle. De bibliotheek gebruikt ContentProvider voor automatische initialisatie — je hoeft de Application-klasse niet aan te passen of code aan MainActivity toe te voegen. De verbinding wordt alleen voor debug-build gemaakt, zodat er geen overbodige code in de release APK zit.
// build.gradle (app/module)
dependencies {
// debugImplementation — bibliotheek alleen voor debug-build
debugImplementation "com.squareup.leakcanary:leakcanary-android:2.14"
}
Na het toevoegen van de afhankelijkheid en herbouwen van het project verschijnt LeakCanary automatisch in de applicatie. Bij de eerste start toont de bibliotheek een systeemmelding over activering. Alle gedetecteerde lekken worden weergegeven als meldingen — klik op de melding opent het scherm met het gedetailleerde rapport (LeakTrace).
Voor aanpassing kun je je eigen AppWatcherInstaller maken en parameters overschrijven: GC-timeout, lijst van gevolgde objecttypen, inschakelen van het opslaan van heap dump op schijf. Voor 90% van de projecten is de standaardconfiguratie echter optimaal.
Vanaf versie 2.12 ondersteunt LeakCanary automatische tracking van ViewModel, coroutine-scopes en State-objecten van Compose. Extra afhankelijkheden zijn niet vereist — de bibliotheek detecteert zelf welke Jetpack-componenten in het project worden gebruikt en activeert de bijbehorende detectoren.
Het LeakCanary-rapport (LeakTrace) — is een meerregelige keten van referenties van GC Root naar het gelekte object. Elke regel toont de klasse en het veld waardoor de sterke referentie loopt. De ontwikkelaar moet de keten van onder naar boven lezen: onderste regel — gelekt object, bovenste — ingangspunt (GC Root).
Een typische LeakTrace ziet er zo uit: GC Root → statisch veld Application → singleton → callback → Activity. Als een ontwikkelaar zo'n keten ziet, is het probleem duidelijk: de singleton houdt een callback vast die een referentie naar Activity heeft vastgelegd. Oplossing — vervang de sterke referentie door een zwakke in de singleton.
┬
├─ 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)
╰
In dit voorbeeld laat LeakCanary zien dat MainActivity wordt vastgehouden via de keten: Application → ArrayList → MainActivity → CallbackWrapper → MyCallback → weer MainActivity. De pijl this$0 geeft aan dat de anonieme klasse MyCallback een externe referentie naar Activity heeft vastgelegd. Oplossing — maak de callback een zwakke referentie of annuleer deze in onDestroy.
LeakCanary toont ook de lekstatus voor elk element van de keten: NO (geen lek — dit is het root-element), YES (object moet worden vernietigd), UNKNOWN (status kon niet worden bepaald). Status UNKNOWN betekent geen probleem — het is een tussenliggend object dat LeakCanary niet eenduidig kan classificeren.
De overgang van versie 1.x naar 2.x was ingrijpend: ontwikkelaars hebben de bibliotheek helemaal herschreven, waarbij de verouderde HAHA-analyzer werd vervangen door een eigen motor Shark geschreven in Kotlin. Shark werkt aanzienlijk sneller, vereist minder geheugen voor analyse en bepaalt nauwkeuriger de hoofdoorzaken van lekken.
| Parameter | LeakCanary 1.x | LeakCanary 2.x |
|---|---|---|
| Analyzertaal | Java (HAHA — fork van Android SDK) | Kotlin (Shark — eigen motor) |
| Installatie | Handmatige configuratie van AppWatcher in Application | Automatisch via ContentProvider |
| Snelheid | 10–30 seconden voor heap dump analyse | 1–5 seconden voor heap dump analyse |
| Prestaties | Neemt 10–50 MB RAM in tijdens analyse | Neemt 2–10 MB RAM in tijdens analyse |
Het belangrijkste voordeel van Shark — het laadt niet de hele heap dump in het geheugen, maar doorloopt de referentiegraf met minimale allocaties. Dit maakt LeakCanary 2.x geschikt voor gebruik op apparaten met weinig RAM zonder risico op OutOfMemoryError tijdens analyse.
In versie 2.x is ook de mogelijkheid toegevoegd om heap dump te exporteren naar een bestand voor latere analyse in Android Studio Memory Profiler. Hiervoor moet je de instelling dumpHeapWhenLeakFound inschakelen in de configuratie van AppWatcher.
LeakCanary detecteert effectief verschillende klassen van lekken die kenmerkend zijn voor Android. Het meest voorkomend is een lek via statische referenties naar Activity — ontwikkelaars bewaren een referentie naar de Activity-context in een singleton, en Activity kan niet door GC worden verzameld na voltooiing van zijn levenscyclus.
De tweede meest voorkomende categorie — lekken via niet-geregistreerde listeners. Als in onStart registerListener is aangeroepen, maar in onStop/onDestroy geen unregisterListener is aangeroepen — wordt het listener-object door het systeem vastgehouden, zelfs na vernietiging van de activiteit. LeakCanary laat ondubbelzinnig zien welke listener en in welke systeemdienst actief is gebleven.
// Typisch lek: Activity vastgelegd in callback van singleton
object AnalyticsManager {
private var callback: ((String) -> Unit)? = null
fun register(callback: (String) -> Unit) {
this.callback = callback // sterke referentie naar callback
}
fun unregister() {
callback = null // VERGEET NIET aan te roepen in onDestroy!
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
AnalyticsManager.register { event ->
logEvent(event) // lambda legt this vast
}
// als je unregister niet aanroept in onDestroy → Activity-lek
}
}
De derde categorie — lekken via Fragment in BackStack. Als FragmentTransaction.addToBackStack() wordt aangeroepen zonder Fragment te verwijderen bij terugkeer, blijven oude Fragment-instanties in het geheugen. LeakCanary helpt dergelijke verborgen lekken in een vroeg stadium van de ontwikkeling te ontdekken.
Voor elk gedetecteerd lek biedt LeakCanary een beschrijving en aanbevelingen voor reparatie. In versie 2.14 is integratie met Android Lint toegevoegd — de bibliotheek kan automatisch taken aanmaken in de issue tracker bij detectie van een lek in CI.
Veelgestelde vragen
Ja, absoluut. LeakCanary wordt aangesloten via debugImplementation in build.gradle, wat het automatisch uitsluit van de release-build. Als je het via implementation aansluit, komt de bibliotheek in de release APK en zal het lekken tonen aan eindgebruikers — dit is onaanvaardbaar.
De impact op prestaties is minimaal. LeakCanary wordt alleen geactiveerd na onDestroy van de component en bemoeit zich niet met het renderen van UI of het verwerken van aanrakingen. De enige kosten — een korte pauze van geforceerde GC (ongeveer 100 ms) en het schrijven van heap dump bij een lek (honderdsten van een seconde).
LeakCanary slaat automatisch de heap dump op in HPROF-formaat in de applicatiemap. Het bestand kan worden geëxporteerd via Android Studio: Device File Explorer → data/data/com.example/files/leakcanary/. Om te bekijken, open je het bestand in Memory Profiler via Capture → Open Heap Dump.
Ja, vanaf versie 2.12 ondersteunt LeakCanary volledig Jetpack Compose. De bibliotheek volgt Composition-contexten en State-objecten, en detecteert automatisch lekken in Composable-functies. Aparte configuratie is niet nodig — het werkt uit de doos.
Vals-positieve meldingen zijn mogelijk, maar zeldzaam. LeakCanary gebruikt een drievoudige GC-aanroep voordat een lek wordt verklaard, wat de meeste valse positieven elimineert. Als je denkt dat een melding vals is — maak dan een IgnoredReference voor de specifieke klasse in de configuratie.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook