Geheugenlek in mobiele applicaties — wat het is, oorzaken en opsporingsmethoden

Auteur: IT Sectr Gepubliceerd: 2026-03-29 Leestijd: 9 min

Een geheugenlek (Memory Leak) — een situatie waarin een applicatie verwijzingen vasthoudt naar objecten die niet meer nodig zijn, waardoor de garbage collector de occupied memory niet kan vrijmaken. Volgens LeakCanary komen zelfs in goed geschreven applicaties 3–5 lekken voor per 10 000 regels code. Elk lek vermindert geleidelijk het beschikbare geheugen, wat leidt tot traagheid en OutOfMemoryError.

Belangrijkste punten

  • Memory Leak — een object blijft in het geheugen, hoewel er geen actieve verwijzingen naar zijn vanuit de applicatielogica
  • Statische verwijzingen naar Activity of Context — de meest voorkomende oorzaak van lekken in Android
  • LeakCanary — de standaard tool voor automatische detectie van lekken in Android
  • WeakReference en Application Context — basistechnieken om lekken te voorkomen
  • Lifecycle-aware componenten elimineren een hele klasse van lekken gerelateerd aan abonnementen

Wat is een geheugenlek

Een geheugenlek (Memory Leak) — is een situatie waarin een object bereikbaar blijft via een keten van sterke verwijzingen (Strong Reference), hoewel het logischerwijs niet langer nodig is voor de applicatie. De garbage collector (GC) beschouwt zo'n object als levend en maakt het geheugen dat het inneemt niet vrij. Als gevolg hiervan neemt het beschikbare heapgeheugen voortdurend af en neemt de frequentie van GC-pauzes toe.

In tegenstelling tot talen met handmatig geheugenbeheer (C, C++), is een lek in Java/Kotlin geen vergeten free(), maar een vergeten verwijzing. Zolang er een strong reference bestaat van het root-object (GC Root) naar het lekkende object, beschouwt GC het als nodig. Typische GC Roots: statische velden, actieve threads, de call-stack, JNI-globale verwijzingen.

Het gevaar van lekken is hun cumulatieve effect. Eén lek van 100 KB is niet merkbaar, maar 100 van zulke lekken nemen 10 MB in beslag en de applicatie begint te vertragen door frequente GC. De kritieke massa van lekken leidt tot OutOfMemoryError en het crashen van de applicatie. Symptomen van een lek: constante toename van het geheugengebruik in de Profiler-grafiek, frequente GC-pauzes met STW (Stop The World) en verminderde UI-prestaties.

Veelvoorkomende soorten lekken in mobiele apps

Vijf soorten lekken dekken 95% van de gevallen in mobiele ontwikkeling. Elk heeft zijn eigen oorzaak en kenmerkend patroon in de code.

Statische verwijzing naar Activity of Context

Het meest bekende lek in Android — het bewaren van een statische verwijzing naar Activity of Context. Typische code: een statisch Activity-veld dat niet wordt gereset bij onDestroy(). Zolang het statische veld leeft, blijft de hele Activity met zijn View-boom, die 1–10 MB kan beslaan, in leven. Dit is een klassiek lek dat LeakCanary als eerste vindt.

Oplossing: bewaar nooit Activity of Context in statische velden. Gebruik Application Context voor singletons die langer leven dan de Activity. Als je een verwijzing naar Activity nodig hebt — gebruik dan WeakReference<Activity>.

kotlin
object MySingleton {
    private var weakActivity: WeakReference<Activity>? = null

    fun attach(activity: Activity) {
        weakActivity = WeakReference(activity)
    }
}

Interne klassen met impliciete verwijzing

Anonieme klassen en niet-statische geneste klassen bevatten impliciet een verwijzing naar de bevattende klasse. Een Runnable die naar een Handler wordt gestuurd en na onDestroy() wordt uitgevoerd, houdt de hele Activity vast. Een Retrofit-callback die de Activity afsluit, doet hetzelfde. Dit is het meest verraderlijke type lek — de impliciete verwijzing is niet zichtbaar in de code.

Kotlin object-expressies en lambda's vangen ook verwijzingen naar de externe klasse. Maak geneste klassen statisch (of top-level in Kotlin) en geef externe verwijzingen door via WeakReference. Gebruik voor lambda's de Lifecycle-aware benadering met viewLifecycleOwner.

Niet-opgezegde listeners en abonnementen

Abonneren op systeemdiensten zonder opzeggen — een direct lek. SensorManager, LocationManager, NotificationListener geregistreerd in onResume() zonder aanroep van unregister in onPause() houden de Activity vast. Vergelijkbaar: RxJava Disposable niet toegevoegd aan CompositeDisposable en een coroutine gestart via GlobalScope.

Gebruik Lifecycle-aware componenten: observe() met LifecycleOwner zegt automatisch op bij onDestroy(). Voor RxJava — viewLifecycleOwner.lifecycle.addObserver met DisposableObserver. Voor coroutines — lifecycleScope.launch() is gekoppeld aan de levenscyclus.

kotlin
// automatisch opzeggen via Lifecycle
viewModel.userData.observe(viewLifecycleOwner) { data ->
    updateUI(data)
}

// coroutines met lifecycleScope
lifecycleScope.launch {
    viewModel.loadData().collect { render(it) }
}

Bitmap zonder recycle

Bitmap neemt aanzienlijke heapgeheugen in beslag: een FullHD-bitmap — 1920 × 1080 × 4 bytes = 8.3 MB. Als er voor elk lijstelement een Bitmap wordt gemaakt en recycle() niet wordt aangeroepen bij verbergen, raakt het geheugen snel uitgeput. Op oude Android-versies (vóór 3.0) werd Bitmap opgeslagen in native geheugen, maar op moderne — in de Dalvik/ART-heap, en GC kan het alleen vrijmaken als er geen strong reference is.

Gebruik Glide of Coil voor het laden van afbeeldingen — deze bibliotheken beheren caching en recycle automatisch. Als je rechtstreeks met Bitmap werkt, roep dan bitmap.recycle() aan voor grote afbeeldingen die niet meer worden weergegeven en gebruik inSampleSize voor het laden van verkleinde kopieën.

Fragment-verwijzing na onDestroyView

Fragment heeft twee levenscycli: die van de Fragment zelf en die van zijn View. Na onDestroyView() wordt de View-boom vernietigd, maar de Fragment zelf kan in het geheugen blijven als er een externe verwijzing bestaat. Typische fout — het bewaren van een verwijzing naar Fragment in een ViewPager-adapter of in de navigatiegraaf die niet wordt opgeruimd bij vernietiging.

Bewaar nooit een verwijzing naar Fragment in velden van langlevende objecten. Gebruik childFragmentManager voor geneste fragmenten en observe() met LifecycleOwner voor gegevensoverdracht tussen hen. ViewPager2 heeft dit probleem op API-niveau opgelost: FragmentTransactionAdapter beheert de levenscyclus correct.

Hoe een geheugenlek te detecteren

Detectie van een lek vereist controle van twee feiten: het geheugen keert niet terug na de expected lifetime en het aantal objecten van een bepaald type neemt toe zonder afname. Het diagnoseproces omvat drie fasen.

Eerste fase — visuele controle via Memory Profiler in Android Studio. Open het tabblad Memory, voer de doelactie uit (open en sluit het scherm), druk op GC (Garbage Collection) en kijk of het geheugen terugkeert naar het oorspronkelijke niveau. Als het geheugen na 3–4 open-sluit cycli gestaag groeit — is er een lek.

Tweede fase — het maken van een Heap Dump. Klik in Memory Profiler op Dump Java Heap. Open het verkregen .hprof-bestand in Android Studio: je ziet alle objecten in de heap met grootte en verwijzingen. Zoek naar klassen waarvan het aantal nul zou moeten zijn na het sluiten van het scherm. Bijvoorbeeld MainActivity met aantal 2 na sluiting — een duidelijk lek.

Derde fase — analyse van Retained Size en GC Root. Analyseer in Android Studio de Retained Size: hoeveel geheugen wordt vrijgemaakt als je dit object verwijdert. Het pad van GC Root naar het object laat zien wat het vasthoudt: Static field → HashMap → Activity — en je ziet het lekpunt. Het Reference widget-paneel toont alle houders van het object.

Tools voor het opsporen van lekken

Vier tools dekken het opsporen van lekken van automatische detectie tot diepgaande Heap Dump-analyse.

ToolMethodeResultaatformaat
LeakCanaryAutomatische monitoringHeap Dump + stack trace van lek
Android Memory ProfilerHandmatige monitoringGeheugengrafiek + Heap Dump
MAT (Eclipse)Diepgaande analyseDominator Tree-rapport + GC Root-pad
PerfettoSystem-wide traceringTijdlijn + native geheugen

LeakCanary — must-have voor elk Android-project. Het detecteert automatisch lekken na afloop van de levenscyclus van Activity/Fragment en toont de exacte locatie van het lek met stack trace. Integratie: één regel in build.gradle. LeakCanary 2.x vereist geen handmatige initialisatie — het registreert automatisch Application Watcher.

Hoe geheugenlekken te voorkomen

Preventie van lekken wordt ingebouwd in het ontwikkelproces via een set regels en tools die de code in elke fase controleren.

De regel van sterke verwijzingen

Bewaar nooit een verwijzing naar Activity, Fragment of View in een statisch veld, singleton of langlevend object. Als een verwijzing onvermijdelijk is — gebruik WeakReference of bewaar gegevens via ViewModel, dat precies zo lang leeft als nodig en View niet direct vasthoudt.

Lifecycle-aware architectuur

ViewModel en LiveData van Android Architecture Components lossen het levenscyclusprobleem op architectuurniveau op. ViewModel overleeft schermrotatie en bevat geen verwijzingen naar View. LiveData zegt automatisch de observer op bij onDestroy(). Gebruik ze in plaats van handmatig abonneren op systeemdiensten.

Code Review met focus op GC Root

Let bij code review op: statische velden met typen Context/View, anonieme klassen, lambda's die Activity afsluiten, handmatige abonnementen, RxJava disposable zonder composite, opslaan van Fragment via Bundle. In Kotlin controleer daarnaast coroutines op launch zonder koppeling aan de levenscyclus.

Automatische controle in CI

LeakCanary kan als onderdeel van de testpijplijn werken: voer acceptatietests uit met LeakCanary en markeer de build als mislukt als er een lek is gevonden. Dit voorkomt dat lekken in productie komen. Vul de controle aan met Android Lint met de regel StaticFieldLeak — deze vindt potentiële lekken op het niveau van statische analyse.

kotlin
// LeakCanary in tests
class LeakTest {
    @Test
    fun activityShouldNotLeak() {
        ActivityScenario.launch(MainActivity::class.java)
            .close()
        LeakAssertions.assertNoLeak() // fail als er een lek is
    }
}

Veelgestelde vragen

Wat is het verschil tussen een geheugenlek en OutOfMemoryError?

Het lek is de oorzaak en OutOfMemoryError is het gevolg. Eén lek leidt niet tot OOM, maar de opeenstapeling van tientallen lekken put de Heap uit. OOM is een fatale uitzondering en een lek is een patroon dat er na verloop van tijd toe leidt.

Hoe vind je een lek zonder LeakCanary?

Via Android Memory Profiler: open en sluit het scherm 5 keer, roep na elke sluiting GC aan. Als het geheugen niet terugkeert naar het basisniveau — is er een lek. Maak een Heap Dump en vind in de lijst de Activity-klasse waarvan het aantal groter is dan 0 na sluiting.

Kan Kotlin lekken op taalkundig niveau voorkomen?

Gedeeltelijk. Kotlin lost het null-safety-probleem op, maar beheert geen strong references. Coroutines met lifecycleScope en viewModelScope voorkomen lekken door achtergrondtaken, en sealed class en data class verminderen het aantal toestanden die tot lekken leiden. De belangrijkste bescherming zijn architectuurpatronen, niet taalfuncties.

Waarom vindt LeakCanary een lek dat er niet is?

LeakCanary geeft soms een false positive: een object kan tijdelijk door het systeem worden vastgehouden (bijvoorbeeld InputMethodManager houdt de laatste View vast). Controleer handmatig: als Retained Size < 1 KB en GC Root een systeemdienst is, is het waarschijnlijk een vals alarm.

Komen geheugenlekken alleen voor op Android?

Nee. Lekken zijn mogelijk op elk platform met GC: iOS (Swift/Objective-C), Flutter (Dart), webbrowsers (JavaScript). De mechanismen zijn hetzelfde — strong reference van GC Root. Op iOS beheert ARC automatisch het geheugen, maar een retain cycle tussen objecten creëert hetzelfde lek.

Samenvatting

  • Memory Leak — een object dat GC niet kan vrijmaken vanwege een vergeten strong reference
  • Statische verwijzingen naar Activity en Context — de meest voorkomende oorzaak van lekken
  • Impliciete verwijzingen via anonieme klassen, lambda's en RxJava-abonnementen zijn verraderlijker dan expliciete
  • LeakCanary vindt automatisch lekken en toont de exacte stack trace
  • Lifecycle-aware componenten (ViewModel, LiveData, lifecycleScope) elimineren een klasse van lekken
  • Heap Dump en Retained Size-analyse — de belangrijkste methode voor handmatige diagnose
  • Preventie omvat code review op strong reference en CI-controle met LeakCanary

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.

Bespreek het project

Lees ook