Automatisch geheugenbeheer via vuilnisophaling is het belangrijkste mechanisme van het Android-platform, gebaseerd op de virtuele machine ART. Volgens Google Android Documentation, 2026, bevrijdt de vuilnisophaler de ontwikkelaar van handmatig geheugenbeheer door automatisch objecten te verwijderen waarnaar geen verwijzingen meer bestaan. Zonder GC zou elke objecttoewijzing een expliciete aanroep van free of delete vereisen, wat in het Java-ecosysteem met miljoenen objecten per seconde fysiek onmogelijk is.
Belangrijkste punten
Garbage Collection (GC) — is een automatisch proces voor het detecteren en vrijgeven van geheugen dat wordt ingenomen door objecten die niet langer door het programma worden gebruikt. In de context van mobiele ontwikkeling wordt GC toegepast op het Android-platform via de virtuele machine ART, evenals in de standaard Java Virtual Machine.
In tegenstelling tot talen met handmatig geheugenbeheer (C, C++), waar de programmeerder expliciet free of delete moet aanroepen, neemt GC de taak van het volgen van de levenscyclus van objecten volledig over. De ontwikkelaar maakt nieuwe objecten via de operator new, en de ophaler bepaalt het moment waarop een object onbereikbaar wordt — dat wil zeggen, er is geen enkele actieve verwijzing meer naar.
De belangrijkste metriek van GC-efficiëntie — pauzetijd (pause time) en doorvoer (throughput). Pauze is de periode waarin de uitvoering van de applicatie wordt onderbroken voor de ophaling. In een mobiele omgeving zijn pauzes langer dan 8–16 milliseconden merkbaar als overgeslagen frames (jank).
Volgens Google I/O 2019 heeft ART in Android 10 de typische GC-pauzes teruggebracht tot 2–4 ms, wat 70% minder is in vergelijking met Dalvik in Android 4.4. Niettemin blijft onjuist omgaan met geheugen — frequente objectallocatie in lussen, het maken van tijdelijke instanties zonder noodzaak — de belangrijkste oorzaak van prestatieproblemen.
Alle GC-implementaties in Java en Android zijn gebaseerd op verschillende fundamentele algoritmen die worden gecombineerd om een balans te vinden tussen pauzetijd en volledigheid van de opschoning. Begrip van deze algoritmen is noodzakelijk voor het schrijven van GC-vriendelijke code.
Mark-and-Sweep — het eenvoudigste algoritme dat in twee fasen werkt. In de Mark-fase doorloopt de ophaler de objectgraaf, beginnend bij de root-verwijzingen (root set) — lokale variabelen, statische velden, thread stacks. Elk bereikbaar object wordt gemarkeerd met een live-vlag. In de Sweep-fase doorloopt de ophaler de volledige heap en geeft het geheugen van ongemarkeerde objecten vrij.
Nadeel — geheugenfragmentatie: na Sweep wisselen vrije gebieden zich af met bezette, wat de toewijzing van grote objecten bemoeilijkt. In mobiele scenario's is dit kritiek omdat de heap meestal klein is (64–512 MB op Android).
Copying Collection verdeelt de heap in twee semi-ruimtes (semi-spaces). Actieve objecten worden van de ene semi-ruimte naar de andere compact gekopieerd, zonder onderbrekingen. Na het kopiëren wordt de oude semi-ruimte volledig vrij verklaard. Het algoritme elimineert volledig fragmentatie, maar vereist twee keer zoveel geheugen.
In mobiele omgevingen wordt Copying Collection gebruikt door generationele ophalers voor het snel opschonen van jonge objecten, die statistisch gezien vroeg sterven (zwakke generatie hypothese).
Generational Collection verdeelt de heap in generaties: Young Generation (jonge objecten) en Old Generation (oude objecten die meerdere ophalingen hebben overleefd). Ophaling van de jonge generatie (Minor GC) wordt vaak en snel uitgevoerd, omdat de meeste objecten jong sterven. Ophaling van de oude generatie (Major GC of Full GC) komt minder vaak voor, maar duurt langer.
// Demonstratie van generationele GC: jonge objecten sterven snel
void processItems(List<Item> items) {
List<Result> results = new ArrayList<>(); // leeft de hele methode
for (Item item : items) {
Result r = new Result(item.getValue()); // sterft onmiddellijk
if (r.isValid()) {
process(r); // r wordt afval
}
}
saveResults(results); // results gaat naar Old Gen
}
In dit voorbeeld worden Result objecten binnen de lus gemaakt en worden onmiddellijk afval — ze zijn ideale kandidaten voor Young GC. Het results object leeft langer en migreert naar Old Generation. De scheiding van generaties stelt Minor GC in staat om jonge objecten in milliseconden op te schonen zonder de oude heap aan te raken.
Android heeft de weg afgelegd van Dalvik VM naar ART (Android Runtime), en de implementatie van GC is een van de belangrijkste verschillen tussen beide. Begrip van de GC-architectuur in Android helpt bij het schrijven van code die pauzes minimaliseert op echte apparaten.
| Kenmerk | Dalvik (tot 4.4) | ART (5.0+) |
|---|---|---|
| GC Type | Mark-and-Sweep met Concurrent Mark | Generational + Concurrent |
| Typische pauze | 10–30 ms | 2–4 ms |
| Compactie | Nee (alleen fragmentatie groeit) | Ja (op de achtergrond, zonder app-onderbreking) |
| AOT-compilatie | JIT (Just-In-Time) | AOT + JIT (hybride) |
Dalvik gebruikte een combinatie van Mark-and-Sweep met een concurrent-fase. Concurrent Mark stelde de applicatie in staat om door te werken tijdens het doorlopen van de objectgraaf, maar de Sweep-fase vereiste het stoppen van alle threads (Stop-The-World). Op apparaten met weinig RAM (512 MB — 1 GB) bereikten de pauzes 30 ms, wat merkbare vertragingen van de interface veroorzaakte. Bovendien compacteerde Dalvik de heap niet, dus na langdurig werk groeide de fragmentatie en het toewijzen van grote objecten (bijv. Bitmap) kon OutOfMemoryError veroorzaken, zelfs met voldoende totale vrije geheugen.
ART (Android Runtime) introduceerde een generationele ophaler met concurrent-compactie. De heap is verdeeld in drie regio's: Young, Mature (analoog aan Old Generation) en Large Object Space (voor objecten groter dan 12 KB). Ophaling van de Young-regio gebeurt parallel zonder het stoppen van threads in de meeste gevallen. In Android 10+ verscheen Concurrent Copying — compactie wordt uitgevoerd op de achtergrondthread zonder Stop-The-World.
Dankzij de ART-architectuur zijn de typische GC-pauzes teruggebracht tot 2–4 ms, en in scenario's met overwegend jonge objecten — tot 0.5–1 ms. Dit stelde Android-apparaten in staat om stabiele 60 FPS te garanderen, zelfs bij actief geheugengebruik.
In het Java-ecosysteem bestaan verschillende GC-implementaties, elk met hun eigen prestatiesprofiel. Voor Android-ontwikkeling is de keuze beperkt tot ART, maar kennis van Java GC is nuttig bij het schrijven van het servergedeelte van mobiele applicaties en bij ontwikkeling met Kotlin Multiplatform.
Serial GC — single-thread ophaler met volledige applicatiestop (Stop-The-World). Elke Mark-, Sweep- en Compact-operatie wordt door één thread uitgevoerd. Prestaties zijn laag — niet geschikt voor mobiele servers. Alleen geschikt voor kleine applicaties met een heap tot 100 MB.
Parallel GC (ook bekend als Throughput Collector) gebruikt meerdere threads voor alle ophalingsfasen. Gericht op maximale doorvoer (throughput) — minimaliseert de tijd besteed aan GC ten opzichte van de applicatietijd. Geactiveerd via de vlag -XX:+UseParallelGC in de JVM.
G1 (Garbage-First) GC — de standaard ophaler in Java 9+. De heap is verdeeld in regio's van 1–32 MB. G1 voorspelt de pauzetijd en probeert binnen de ingestelde limiet te blijven (standaard 200 ms). Prioriteit: eerst worden regio's met het meeste afval opgeschoond (vandaar de naam). G1 is efficiënt voor servers met grote heap (4–64 GB) en voorspelbare pauzes.
// Inschakelen van G1 GC met een doelpauze van 100 ms
// java -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -jar app.jar
public class MemoryMonitor {
private static final long THRESHOLD = 512 * 1024 * 1024; // 512 MB
public void checkHeapUsage() {
Runtime rt = Runtime.getRuntime();
long used = rt.totalMemory() - rt.freeMemory();
if (used > THRESHOLD) {
System.out.println("Heap-gebruiksdrempel overschreden: " + used);
System.out.println("Overweeg allocaties te verminderen");
}
}
}
Monitoring van de heap via Runtime maakt vroege detectie van geheugenlekken mogelijk. Als used 80% van de maximale heap overschrijdt bij stabiel werk — is dit een signaal van een mogelijk lek of overmatig geheugengebruik door de applicatie.
Zelfs moderne ART GC lost niet alle problemen op — onjuist gebruik van geheugen blijft de belangrijkste oorzaak van jank en ANR (Application Not Responding). Laten we de belangrijkste scenario's en optimalisatiemethoden bekijken.
GC Pauses — het stoppen van applicatie-threads tijdens de ophaling. Op het scherm verschijnt dit als overgeslagen frames, wanneer de tijd tussen twee frames 16.6 ms (60 FPS) overschrijdt. Als GC 30 ms duurt, wordt slechts één frame getekend in plaats van twee — de gebruiker ziet haperingen van de interface.
Belangrijkste oorzaken van lange pauzes: groot aantal levende objecten in Old Generation, heap-fragmentatie, frequente Full GC. Voor diagnostiek worden Android Studio Profiler en systrace gebruikt.
De hoofdregel van GC-vriendelijke code — minimaliseer het aantal gealloceerde objecten. Elk nieuw object vereist niet alleen geheugentoewijzing, maar ook latere ophaling. Zelfs als GC snel is, geven 1000 onnodige allocaties per seconde 1000 controles voor de ophaler.
Een geheugenlek ontstaat wanneer een object bereikbaar blijft, hoewel het niet meer nodig is. GC kan zo'n object niet verwijderen en het geheugen raakt geleidelijk uitgeput. Typische oorzaken: niet-afgemelde luisteraars, statische verwijzingen naar Activity, anonieme klassen die externe context vastleggen en niet-gesloten Cursor/InputStream.
// Geheugenlek: anonieme klasse houdt verwijzing naar Activity vast
public void startTask() {
new Thread(new Runnable() { // houdt impliciet this (Activity) vast
@Override
public void run() {
// langdurige bewerking...
System.out.println("Gereed");
}
}).start();
}
// Correctie: statische geneste klasse + WeakReference
private static class TaskRunnable implements Runnable {
private WeakReference<Activity> activityRef;
TaskRunnable(Activity activity) {
this.activityRef = new WeakReference<>(activity);
}
@Override
public void run() {
Activity act = activityRef.get();
if (act != null) {
// veilig werken met Activity
}
}
}
In dit voorbeeld legt de anonieme Runnable een impliciete verwijzing naar Activity vast. Zolang de thread leeft — kan Activity niet door GC worden opgehaald, zelfs niet als de gebruiker het scherm al heeft gesloten. De correctie WeakReference + static class verbreekt deze keten en maakt het mogelijk Activity te gebruiken.
Veelgestelde vragen
GC in Android (ART) is een generationele ophaler met concurrent-compactie, geoptimaliseerd voor mobiele apparaten met beperkt geheugen. Java GC (G1, ZGC) zijn server-ophalers met grote heaps en voorspelbare pauzes. ART GC gebruikt geen JVM-vlaggen — alle configuratie gebeurt automatisch op OS-niveau.
Stop-The-World — het moment waarop de ophaler alle applicatie-threads onderbreekt om veilig de objectgraaf te doorlopen of geheugen vrij te maken. Hoe langer STW, hoe merkbaarder de jank. ART heeft de typische STW-tijd teruggebracht tot 2–4 ms dankzij de generationele architectuur.
Gebruik Android Studio Memory Profiler — het toont heapgroei, aantal allocaties en maakt Heap Dump mogelijk. Voor diepgaande analyse gebruik LeakCanary — de bibliotheek detecteert automatisch lekken en toont de referentieketen die GC-ophaling verhindert.
Full GC — volledige ophaling van alle heap-generaties, inclusief Old Generation. In mobiele applicaties kan Full GC 50–200 ms duren, wat merkbare jank of ANR veroorzaakt. Belangrijkste oorzaken: heap-fragmentatie, geheugenlekken, overschrijding van de Old Generation-drempel.
Kotlin biedt coroutines met gestructureerde concurrentie — annulering van de scope annuleert automatisch alle onderliggende coroutines en voorkomt lekken. Ook heeft Kotlin de lazy-delegate voor luie initialisatie en scope-operators die het aantal tijdelijke objecten verminderen.
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