Die automatische Speicherverwaltung durch Speicherbereinigung ist ein Schlüsselmechanismus der Android-Plattform, basierend auf der virtuellen Maschine ART. Laut Google Android Documentation, 2026, befreit der Garbage Collector den Entwickler von der manuellen Speicherverwaltung, indem er automatisch Objekte entfernt, auf die keine Referenzen mehr zeigen. Ohne GC würde jede Objektzuweisung einen expliziten Aufruf von free oder delete erfordern, was im Java-Ökosystem mit Millionen von Objekten pro Sekunde physikalisch unmöglich ist.
Wichtigste Erkenntnisse
Garbage Collection (GC) ist ein automatischer Prozess zur Erkennung und Freigabe von Speicher, der von Objekten belegt wird, die vom Programm nicht mehr verwendet werden. Im Kontext der mobilen Entwicklung wird GC auf der Android-Plattform über die virtuelle Maschine ART sowie in der standardmäßigen Java Virtual Machine verwendet.
Im Gegensatz zu Sprachen mit manueller Speicherverwaltung (C, C++), bei denen der Programmierer explizit free oder delete aufrufen muss, übernimmt GC vollständig die Aufgabe, den Lebenszyklus von Objekten zu verfolgen. Der Entwickler erstellt neue Objekte über den new-Operator, während der Collector bestimmt, wann ein Objekt unerreichbar wird — das heißt, wenn keine aktiven Referenzen mehr darauf vorhanden sind.
Die Hauptmetriken der GC-Effizienz sind die Pausenzeit (pause time) und der Durchsatz (throughput). Die Pause ist der Zeitraum, in dem die Anwendungsausführung für die Sammlung angehalten wird. In einer mobilen Umgebung sind Pausen von mehr als 8–16 Millisekunden als ausgelassene Frames (Jank) wahrnehmbar.
Laut Google I/O 2019 reduzierte ART in Android 10 die typischen GC-Pausen auf 2–4 ms, was 70% weniger im Vergleich zu Dalvik in Android 4.4 entspricht. Dennoch bleibt unsachgemäße Speicherverwaltung — häufige Objektallokation in Schleifen, unnötige Erstellung temporärer Instanzen — die Hauptursache für Leistungsprobleme.
Alle GC-Implementierungen in Java und Android basieren auf mehreren grundlegenden Algorithmen, die kombiniert werden, um ein Gleichgewicht zwischen Pausenzeit und Gründlichkeit der Bereinigung zu erreichen. Das Verständnis dieser Algorithmen ist für das Schreiben von GC-freundlichem Code unerlässlich.
Mark-and-Sweep ist der einfachste Algorithmus, der in zwei Phasen arbeitet. In der Mark-Phase durchläuft der Collector den Objektgraphen beginnend mit den Wurzelreferenzen (Root Set) — lokalen Variablen, statischen Feldern, Thread-Stacks. Jedes erreichbare Objekt wird mit einem Live-Flag markiert. In der Sweep-Phase durchläuft der Collector den gesamten Heap und gibt den Speicher nicht markierter Objekte frei.
Der Nachteil ist die Speicherfragmentierung: Nach Sweep wechseln sich freie Bereiche mit belegten ab, was die Allokation großer Objekte erschwert. In mobilen Szenarien ist dies kritisch, da der Heap in der Regel klein ist (64–512 MB auf Android).
Copying Collection teilt den Heap in zwei Halbräume (Semi-Spaces). Aktive Objekte werden lückenlos von einem Halbraum in den anderen kompakt kopiert. Nach dem Kopieren wird der alte Halbraum vollständig als frei deklariert. Der Algorithmus beseitigt die Fragmentierung vollständig, benötigt jedoch doppelt so viel Speicher.
In mobilen Umgebungen wird Copying Collection von generationalen Collectors zur schnellen Bereinigung junger Objekte verwendet, die statistisch gesehen früh sterben (schwache Generationshypothese).
Generational Collection unterteilt den Heap in Generationen: Young Generation (junge Objekte) und Old Generation (alte Objekte, die mehrere Sammlungen überlebt haben). Die Sammlung der jungen Generation (Minor GC) wird häufig und schnell durchgeführt, da die meisten Objekte jung sterben. Die Sammlung der alten Generation (Major GC oder Full GC) erfolgt seltener, dauert aber länger.
// Demonstration des generationellen GC: Junge Objekte sterben schnell
void processItems(List<Item> items) {
List<Result> results = new ArrayList<>(); // lebt für die gesamte Methode
for (Item item : items) {
Result r = new Result(item.getValue()); // stirbt sofort
if (r.isValid()) {
process(r); // r wird zu Müll
}
}
saveResults(results); // results wechselt zu Old Gen
}
In diesem Beispiel werden Result-Objekte innerhalb einer Schleife erstellt und werden sofort zu Müll — sie sind ideale Kandidaten für Young GC. Das Objekt results lebt länger und wandert in die Old Generation. Die Trennung der Generationen ermöglicht es Minor GC, junge Objekte in Millisekunden zu bereinigen, ohne den alten Heap zu berühren.
Android hat sich von Dalvik VM zu ART (Android Runtime) weiterentwickelt, und die GC-Implementierung ist einer der Hauptunterschiede zwischen ihnen. Das Verständnis der GC-Architektur in Android hilft, Code zu schreiben, der Pausen minimiert auf realen Geräten.
| Eigenschaft | Dalvik (bis 4.4) | ART (5.0+) |
|---|---|---|
| GC-Typ | Mark-and-Sweep mit Concurrent Mark | Generational + Concurrent |
| Typische Pause | 10–30 ms | 2–4 ms |
| Kompaktierung | Nein (nur Fragmentierung wächst) | Ja (im Hintergrund, ohne App-Stop) |
| AOT-Kompilierung | JIT (Just-In-Time) | AOT + JIT (Hybrid) |
Dalvik verwendete eine Kombination aus Mark-and-Sweep mit einer konkurrierenden Phase. Concurrent Mark erlaubte der Anwendung, während der Traversierung des Objektgraphen weiterzuarbeiten, aber die Sweep-Phase erforderte das Stoppen aller Threads (Stop-The-World). Auf Geräten mit wenig RAM (512 MB — 1 GB) erreichten die Pausen 30 ms, was zu spürbaren Verzögerungen der Benutzeroberfläche führte. Außerdem kompaktierte Dalvik den Heap nicht, so dass nach längerer Nutzung die Fragmentierung zunahm und die Allokation großer Objekte (z.B. Bitmap) einen OutOfMemoryError auslösen konnte, selbst wenn insgesamt genügend freier Speicher vorhanden war.
ART (Android Runtime) führte einen generationalen Collector mit konkurrierender Kompaktierung ein. Der Heap ist in drei Regionen unterteilt: Young, Mature (analog zur Old Generation) und Large Object Space (für Objekte größer als 12 KB). Die Sammlung der Young Region erfolgt in den meisten Fällen parallel ohne Thread-Stopps. In Android 10+ wurde Concurrent Copying eingeführt — die Kompaktierung läuft in einem Hintergrundthread ohne Stop-The-World.
Dank der ART-Architektur wurden typische GC-Pausen auf 2–4 ms reduziert, und in Szenarien mit überwiegend jungen Objekten auf 0.5–1 ms. Dies ermöglichte es Android-Geräten, stabile 60 FPS auch bei aktiven Speicheroperationen beizubehalten.
Im Java-Ökosystem gibt es mehrere GC-Implementierungen, jede mit ihrem eigenen Leistungsprofil. Für die Android-Entwicklung ist die Wahl auf ART beschränkt, aber Kenntnisse über Java GC sind beim Schreiben von serverseitigem Code für mobile Anwendungen und bei der Entwicklung mit Kotlin Multiplatform nützlich.
Serial GC ist ein Single-Thread-Collector mit vollständigem Anwendungsstopp (Stop-The-World). Jede Mark-, Sweep- und Compact-Operation wird von einem Thread ausgeführt. Die Leistung ist gering — wird nicht für mobile Server verwendet. Geeignet nur für kleine Anwendungen mit einem Heap von bis zu 100 MB.
Parallel GC (auch bekannt als Throughput Collector) verwendet mehrere Threads für alle Sammlungsphasen. Er ist auf maximalen Durchsatz (throughput) ausgerichtet — minimiert die Zeit, die für GC im Verhältnis zur Anwendungslaufzeit aufgewendet wird. Aktiviert über das Flag -XX:+UseParallelGC in der JVM.
G1 (Garbage-First) GC ist der Standard-Collector in Java 9+. Der Heap ist in Regionen von 1–32 MB unterteilt. G1 sagt die Pausenzeit voraus und bemüht sich, innerhalb eines festgelegten Limits (Standard 200 ms) zu bleiben. Priorität: Regionen mit dem meisten Müll werden zuerst bereinigt (daher der Name). G1 ist effektiv für Server mit großem Heap (4–64 GB) mit vorhersagbaren Pausen.
// Aktivierung von G1 GC mit Zielpause von 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 usage exceeded threshold: " + used);
System.out.println("Consider reducing allocations");
}
}
}
Die Überwachung des Heaps über Runtime ermöglicht die frühzeitige Erkennung von Speicherlecks. Wenn used bei stabilem Betrieb 80% des maximalen Heaps überschreitet — ist dies ein Signal für ein mögliches Leck oder einen übermäßigen Speicherverbrauch der Anwendung.
Selbst moderner ART GC löst nicht alle Probleme — unsachgemäße Speichernutzung bleibt die Hauptursache für Jank und ANR (Application Not Responding). Betrachten wir die wichtigsten Szenarien und Optimierungsmethoden.
GC-Pausen — Stopps der Anwendungsthreads während der Sammlung. Auf dem Bildschirm äußert sich dies als ausgelassene Frames, wenn die Zeit zwischen zwei Frames 16.6 ms (60 FPS) überschreitet. Wenn GC 30 ms dauert, wird nur ein Frame statt zwei gezeichnet — der Benutzer sieht Ruckeln in der Benutzeroberfläche.
Die Hauptursachen für lange Pausen: eine große Anzahl lebender Objekte in der Old Generation, Heap-Fragmentierung, häufige Full GC. Zur Diagnose werden Android Studio Profiler und systrace verwendet.
Die Hauptregel für GC-freundlichen Code ist die Minimierung der Anzahl allokierter Objekte. Jedes neue Objekt erfordert nicht nur Speicherzuweisung, sondern auch anschließende Sammlung. Selbst wenn GC schnell ist, führen 1000 zusätzliche Allokationen pro Sekunde zu 1000 Überprüfungen durch den Collector.
Ein Speicherleck tritt auf, wenn ein Objekt erreichbar bleibt, obwohl es nicht mehr benötigt wird. GC kann ein solches Objekt nicht löschen und der Speicher wird allmählich erschöpft. Typische Ursachen: nicht abgemeldete Listener, statische Referenzen auf Activity, anonyme Klassen, die den externen Kontext erfassen, und nicht geschlossene Cursor/InputStream.
// Speicherleck: Anonyme Klasse hält Referenz auf Activity
public void startTask() {
new Thread(new Runnable() { // hält implizit this (Activity)
@Override
public void run() {
// lange Operation...
System.out.println("Done");
}
}).start();
}
// Behebung: Statische verschachtelte 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) {
// sichere Arbeit mit Activity
}
}
}
In diesem Beispiel erfasst der anonyme Runnable eine implizite Referenz auf die Activity. Solange der Thread lebt — kann die Activity nicht vom GC gesammelt werden, selbst wenn der Benutzer den Bildschirm bereits geschlossen hat. Die Korrektur mit WeakReference + statischer Klasse unterbricht diese Kette und ermöglicht die Freigabe der Activity.
Häufig gestellte Fragen
GC in Android (ART) ist ein generationaler Collector mit konkurrierender Kompaktierung, optimiert für mobile Geräte mit begrenztem Speicher. Java GC (G1, ZGC) sind serverseitige Collectors mit großen Heaps und vorhersagbaren Pausen. ART GC verwendet keine JVM-Flags — die gesamte Abstimmung erfolgt automatisch auf OS-Ebene.
Stop-The-World ist der Moment, in dem der Collector alle Anwendungsthreads anhält, um den Objektgraphen sicher zu durchlaufen oder Speicher freizugeben. Je länger das STW, desto deutlicher ist das Jank. ART hat die typische STW-Zeit auf 2–4 ms reduziert, dank seiner generationellen Architektur.
Verwenden Sie den Android Studio Memory Profiler — er zeigt das Heap-Wachstum, die Anzahl der Allokationen und ermöglicht Heap Dumps. Für eine tiefgehende Analyse verwenden Sie LeakCanary — die Bibliothek erkennt Lecks automatisch und zeigt die Referenzkette, die die GC-Sammlung verhindert.
Full GC ist eine vollständige Sammlung aller Heap-Generationen, einschließlich der Old Generation. In mobilen Anwendungen kann Full GC 50–200 ms dauern und zu spürbarem Jank oder ANR führen. Hauptursachen: Heap-Fragmentierung, Speicherlecks, Überschreiten der Old-Generation-Schwelle.
Kotlin bietet Koroutinen mit strukturierter Nebenläufigkeit — die Bereichsabbrüche brechen automatisch alle untergeordneten Koroutinen ab und verhindern so Lecks. Kotlin hat auch den lazy-Delegaten für die verzögerte Initialisierung und Bereichsfunktionen, die die Anzahl temporärer Objekte reduzieren.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch