OutOfMemoryError — een fatale uitzondering die optreedt wanneer de Java Virtual Machine (JVM) of Android Runtime (ART) geen geheugen kan toewijzen voor een nieuw object vanwege ruimtegebrek in de heap. Volgens Square Engineering wordt 70% van de OutOfMemoryError-gevallen in mobiele apps veroorzaakt door geheugenlekken, niet door daadwerkelijke overschrijding van de limiet. Inzicht in de oorzaken van OOM is de sleutel tot stabiele werking van de app.
Belangrijkste punten
OutOfMemoryError (OOM) — een uitzondering uit de VirtualMachineError-familie in Java/Kotlin die aangeeft dat er geen geheugen kan worden toegewezen voor een nieuw object. In tegenstelling tot checked-uitzonderingen is OOM een Error en vereist het geen afhandeling via catch — hoewel het technisch mogelijk is om het op te vangen. Na het optreden van OOM verkeert de app meestal in een instabiele toestand en wordt aanbevolen deze te sluiten.
Op Android heeft elke app een Heap-limiet die is ingesteld door de fabrikant van het apparaat. Voor moderne smartphones met 6+ GB RAM is de limiet 256–512 MB, voor budgetapparaten — 128–192 MB. Wanneer het totale volume van alle levende objecten deze limiet overschrijdt, gooit ART een OutOfMemoryError.
Belangrijk om te begrijpen: OOM betekent niet altijd dat het fysieke geheugen van het apparaat op is. Het betekent dat de app zijn door het systeem ingestelde Heap-limiet heeft uitgeput. Andere apps kunnen vrij geheugen hebben, maar jouw app kan dit niet gebruiken vanwege procesisolatie in Android.
Vijf scenario’s leiden regelmatig tot OOM in mobiele apps. Elk scenario is gekoppeld aan een specifiek gegevenstype of bewerking.
Bitmap — de grootste geheugenverbruiker in Android-apps. Het laden van een FullHD-afbeelding (1920 × 1080) in originele grootte neemt 8.3 MB in beslag in ARGB_8888-formaat. Als er 50 van dergelijke afbeeldingen in een RecyclerView staan — is dat 415 MB, wat de Heap van elk apparaat overschrijdt. Het laden van afbeeldingen zonder inSampleSize — gegarandeerde OOM op zwakke apparaten.
Gebruik Glide of Coil voor automatische schaling. Deze bibliotheken laden afbeeldingen op een formaat dat past bij de View, niet de originele resolutie. Gebruik voor directe BitmapFactory.Options inSampleSize: bereken dit als een macht van twee zodat het uiteindelijke formaat niet groter is dan 2048 × 2048 pixels. Gebruik daarnaast RGB_565 in plaats van ARGB_8888 voor afbeeldingen zonder transparantie — dit halveert het geheugengebruik.
fun loadScaledBitmap(path: String, reqWidth: Int): Bitmap? {
val opts = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeFile(path, opts)
opts.inSampleSize = calculateSampleSize(opts.outWidth, reqWidth)
opts.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, opts)
}
Eén lek van een paar KB veroorzaakt geen OOM. Maar tientallen lekken op elk scherm stapelen zich op: elke overgang naar een scherm voegt een lek toe, de GC kan objecten niet vrijmaken en de Heap raakt vol. Typisch patroon: een gebruiker opent en sluit het profielscherm 20 keer → Heap groeit met 200 MB → de app crasht met OOM.
Installeer LeakCanary in het project voor automatische detectie van lekken. Het zal elk gelekt object tonen met een exacte stack trace. Na het oplossen van alle lekken wordt het Heap-gebruik stabiel: na het sluiten van het scherm keert het geheugen terug naar het basisniveau.
Het laden van bestanden in hun geheel in byte[] — de directe weg naar OOM. Een JSON-bestand van 50 MB zal bij het parsen een string van dezelfde grootte plus een DOM-model creëren. Videobestanden die in het geheugen worden geladen, audiobuffers en grote protobuf-datasets — ze kunnen allemaal de Heap-limiet in één bewerking overschrijden.
Verwerk grote gegevens met stromen: InputStream met een buffer van 4–8 KB, streaming JSON-parser (Jackson of Gson met JsonReader), MediaCodec voor video. Roep nooit File.readBytes() aan op bestanden groter dan 10% van de beschikbare Heap.
Intensief maken van objecten in een lus zonder tussentijdse GC kan leiden tot OOM, vooral op apparaten met een kleine Heap. Voorbeeld: het genereren van 100.000 objecten in een for-lus die niet in de Heap passen voordat de GC de kans heeft ze te verzamelen. Dit komt vaker voor in games en grafische editors.
Gebruik een Object Pool voor objecten die massaal worden gemaakt en vernietigd. Gebruik voor numerieke gegevens primitieve typen (FloatArray in plaats van List<Float>). RecyclerView met ViewHolder Pool lost dit probleem op voor UI-componenten.
Fragmentatie — een toestand waarbij er in totaal voldoende vrij geheugen is, maar er geen aaneengesloten blok is voor een nieuw object. ART compacteret de Heap tijdens GC, maar niet altijd met succes. Grote arrays (Bitmap, byte[]) zijn het meest gevoelig voor fragmentatie.
ART op Android 8+ gebruikt Generational GC, wat fragmentatie vermindert door jonge en oude objecten te scheiden. Vermijd desondanks het toewijzen van fragmenten van verschillende grootte in dezelfde pool — probeer vooraf toegewezen buffers van vaste grootte te gebruiken.
De Heap-limiet in Android is geen constante — deze hangt af van de fabrikant, het apparaatmodel en de OS-versie. Google stelt minimale vereisten via de Compatibility Definition Document (CDD), maar fabrikanten bepalen de werkelijke waarden.
| Apparaatcategorie | Typische Heap | largeHeap |
|---|---|---|
| Budget (1–2 GB RAM) | 128–192 MB | 256–384 MB |
| Middenklasse (3–4 GB RAM) | 256–384 MB | 512 MB |
| Vlaggenschip (6+ GB RAM) | 384–512 MB | 768 MB–1 GB |
| Tablets (4+ GB RAM) | 256–512 MB | 768 MB |
| Wear OS | 32–64 MB | Niet |
Een verhoogde limiet kan worden aangevraagd via android:largeHeap="true" in het manifest. Gebruik het voorzichtig: het vergroten van de Heap lost het lekprobleem niet op en kan de gebruikerservaring verslechteren als het systeem gedwongen wordt andere apps te doden om geheugen vrij te maken. Voor Wear OS is de Heap-limiet minimaal — slechts 32–64 MB, hier is largeHeap niet beschikbaar en is geheugenbesparing dubbel kritiek.
Diagnose van OOM vereist analyse van de Heap Dump en inzicht in welke objecten geheugen verbruiken. Android Studio biedt alle benodigde hulpmiddelen.
Stap 1: Leg het OOM-moment vast. Druk in Android Memory Profiler op Record memory allocations en voer het scenario uit dat de crash veroorzaakt. De profiler toont een piek in allocaties vlak voor OOM. Als OOM niet reproduceerbaar is, verklein dan de Heap via android:smallHeap in een debug-build of gebruik DDMS met handmatige GC-aanroep.
Stap 2: Maak een Heap Dump op het moment van piekbelasting (vlak voor OOM). Open de Dump in Android Studio: het tabblad Classes is gesorteerd op Retained Size. De grootste objecten — Bitmap, byte[], String. Controleer voor elke Bitmap de grootte (width × height × 4 bytes) en het laadpad via Stack Trace.
Stap 3: Analyseer het aantal herhalende objecten. Als je 200 identieke Fragmenten of Activiteiten ziet — is dat een lek. Als er 500 Bitmaps van dezelfde grootte zijn — een probleem met het cachen van afbeeldingen. MAT (Memory Analyzer Tool) biedt een diepere analyse met Dominator Tree, die laat zien welke objecten 80% van de Heap vasthouden.
// Opdracht voor Heap Dump via adb
adb shell am dumpheap com.example.app /data/local/tmp/dump.hprof
adb pull /data/local/tmp/dump.hprof.
Een uitgebreide strategie om OOM te voorkomen omvat vijf beschermingsniveaus: van architectuuroplossingen tot monitoring in productie.
ViewModel + Repository patroon scheidt gegevens van de UI en voorkomt het vasthouden van de View bij rotatie van het scherm. ViewModel overleeft Activity, de gegevens gaan niet verloren en de View kan opnieuw worden gemaakt zonder duplicatie van gegevens in het geheugen. Gebruik StateFlow in plaats van LiveData voor expliciet statusbeheer.
Glide — de verplichte bibliotheek voor het werken met afbeeldingen. Het schaalt automatisch, cached (schijf + geheugen) en recyclede Bitmap. Configureer diskCacheStrategy en skipMemoryCache voor grote lijsten. Gebruik voor geanimeerde afbeeldingen Glide met GIF/WebP — ze nemen minder geheugen in beslag dan een reeks Bitmaps.
Firebase Performance Monitoring volgt het geheugengebruik in realtime. Stel een alert in wanneer de Heap meer dan 80% van de limiet bezet — dit is een signaal om te controleren. Crashlytics verzamelt OOM als een uitzondering en toont de laatste bekende Heap-status voor de crash. Gebruik voor Android 11+ ApplicationExitInfo voor detectie van OOM-beëindigingen.
Test de app verplicht op apparaten met minimale Heap (128–192 MB). Een emulator met een klein scherm en kleine Heap emuleert een budgetapparaat. Als de app op zo’n apparaat werkt, zullen er op vlaggenschepen geen OOM-problemen zijn. Gebruik Firebase Test Lab met echte apparaten uit verschillende prijscategorieën.
// Controleer beschikbare Heap voor zware bewerking
fun canAllocate(requiredBytes: Long): Boolean {
val runtime = Runtime.getRuntime()
val free = runtime.freeMemory()
return free > requiredBytes * 2 // reserve 50%
}
Veelgestelde vragen
Technisch ja, maar het wordt niet aanbevolen. Na OOM verkeert de app in een instabiele toestand: nieuwe allocaties werken mogelijk niet en sommige objecten kunnen gedeeltelijk zijn aangemaakt. De enige redelijke actie in catch — loggen en de Activity herstarten.
De Heap-limiet verschilt per apparaat. Een bewerking die 300 MB nodig heeft, zal crashen op een apparaat met een limiet van 192 MB, maar werken op een vlaggenschip met 512 MB. Test op apparaten met minimale specificaties om OOM-scenario’s te ontdekken.
largeHeap verhoogt de limiet, maar versnelt de app niet. GC-pauzes worden langer omdat het verzamelen van een grote Heap langer duurt. Het systeem kan achtergrondapps doden om geheugen vrij te maken. Gebruik largeHeap alleen voor apps die objectief veel geheugen nodig hebben (camera’s, editors).
OOM — een uitzondering binnen de app bij onvoldoende Heap. Het door het systeem doden (Low Memory Killer) — een beslissing van de Linux-kernel om een proces te doden om geheugen vrij te maken voor andere apps. Bij het doden door het systeem ontvangt de app geen uitzondering — het proces wordt gewoon beëindigd.
Formule: width × height × bytesPerPixel. ARGB_8888 = 4 B/pixel, RGB_565 = 2 B/pixel. FullHD Bitmap (1920 × 1080) in ARGB_8888 = 8.3 MB. 4K Bitmap (3840 × 2160) = 33 MB. Schaal afbeeldingen altijd naar het formaat dat nodig is voor weergave op het scherm.
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