Lexikon · G

Garbage Collection

Automatische Freigabe von Speicher, den ein Programm nicht mehr nutzt, durchgeführt von der Laufzeitumgebung.

Von Obseria-RedaktionAktualisiert

Definition

Garbage Collection (GC) ist eine automatische Speicherverwaltung, bei der die Laufzeitumgebung Objekte erkennt, die vom Programm aus nicht mehr erreichbar sind, und deren Speicher freigibt. Das verhindert ganze Fehlerklassen wie Use-after-free, kostet aber CPU-Zeit und kann Anwendungs-Threads pausieren, was sich als höhere Tail-Latenz zeigt.

Was Garbage Collection ist

In Sprachen wie Java, Kotlin, C#, Go, JavaScript und Python legen Entwickler Objekte an, geben sie aber nicht explizit frei. Der Garbage Collector der Laufzeitumgebung ermittelt regelmäßig, welche Objekte von Wurzeln wie Thread-Stacks, globalen Variablen und Registern aus noch erreichbar sind, und gibt den Speicher aller übrigen frei. Die meisten modernen Collectoren arbeiten tracing: Sie markieren erreichbare Objekte und räumen den Rest auf oder verdichten ihn. CPython ist die bekannte Ausnahme und nutzt Referenzzählung plus einen Zyklus-Collector.

Für Observability ist GC relevant, weil sie CPU verbraucht und je nach Collector Anwendungs-Threads kurz anhält („Stop-the-World“-Pausen). Beides zeigt sich als langsamere Requests, vor allem im 99. Perzentil, und unter Speicherdruck als geringerer Durchsatz.

Grundbegriffe

  • Generationshypothese. Die meisten Objekte sterben jung. Generationelle Collectoren führen eine junge Generation, die oft und günstig aufgeräumt wird, und eine alte, die seltener drankommt.
  • Stop-the-World oder nebenläufig. Manche Phasen halten alle Anwendungs-Threads an; nebenläufige Collectoren markieren größtenteils, während die Anwendung läuft, auf Kosten zusätzlicher CPU und Barrieren.
  • Verschiebend oder nicht verschiebend. Kompaktierende oder kopierende Collectoren verschieben Objekte gegen Fragmentierung, nicht verschiebende lassen sie an Ort und Stelle.
  • Allokationsrate. Je schneller ein Programm alloziert, desto öfter muss der Collector laufen. Weniger Allokationen sind oft das wirksamste GC-Tuning.

So arbeiten die wichtigsten Laufzeitumgebungen

JVM (Java, Kotlin, Scala)

Die JVM bietet mehrere Collectoren. G1 ist seit JDK 9 für die meisten Serverkonfigurationen Standard; er arbeitet generationell, teilt den Heap in Regionen und verfolgt ein konfigurierbares Pausenziel (-XX:MaxGCPauseMillis, standardmäßig 200 ms). ZGC und Shenandoah sind Collectoren mit sehr kurzen Pausen, die fast alle Arbeit einschließlich Kompaktierung nebenläufig erledigen; Pausen liegen typischerweise im Bereich unter einer bis wenigen Millisekunden, unabhängig von der Heap-Größe. ZGC wurde mit JDK 21 generationell, seit JDK 23 ist der generationelle Modus Standard. Parallel GC optimiert auf Durchsatz und nimmt längere Pausen in Kauf, Serial GC eignet sich für kleine Heaps.

.NET

Der .NET-GC arbeitet generationell mit den Generationen 0, 1 und 2, dazu kommen ein Large Object Heap für Objekte ab 85.000 Bytes und seit .NET 5 ein Pinned Object Heap. Gen-0- und Gen-1-Collections sind kurz und halten die Welt an, Gen 2 kann als Background GC parallel zur Anwendung laufen. Workstation GC zielt auf Client-Anwendungen, Server GC nutzt für mehr Durchsatz einen Heap und GC-Thread pro Kern. Seit .NET 9 passt Server GC die Zahl der Heaps standardmäßig an die Last an (DATAS).

Go

Go nutzt einen nebenläufigen Tri-Color-Mark-and-Sweep-Collector, der nicht generationell und nicht verschiebend arbeitet. Stop-the-World-Phasen sind kurz, meist deutlich unter einer Millisekunde, doch das Markieren kostet CPU und kann stark allozierende Goroutinen über Mark Assists bremsen. Gesteuert wird über zwei Stellschrauben: GOGC (Standard 100, der Heap darf bis zum nächsten Zyklus um rund 100 % des lebenden Heaps wachsen) und GOMEMLIMIT (ein weiches Speicherlimit seit Go 1.19). Mit Go 1.26 ist der Green-Tea-Collector standardmäßig aktiv, der die Speicherlokalität beim Markieren verbessert.

Was Sie überwachen sollten

SignalWarum es zählt
Dauer der GC-Pausen (Histogramm)Direkter Beitrag zur Tail-Latenz
Anteil der CPU-Zeit für GCKapazität, die an die Speicherverwaltung verloren geht
Belegter Heap nach GCDie lebende Menge; stetiger Anstieg deutet auf ein Leck
AllokationsrateBestimmt die GC-Häufigkeit
Anzahl der Collections nach Generation oder TypHäufige Old-Generation- oder Full-GCs zeigen Speicherdruck

Die OpenTelemetry Semantic Conventions definieren Runtime-Metriken für diese Signale, etwa jvm.gc.duration und jvm.memory.used_after_last_gc für die JVM, mit Entsprechungen für .NET und Go. Korrelieren Sie sie mit der Latenz der Requests: Fallen p99-Spitzen mit GC-Pausen zusammen, ist der Collector Teil der Erklärung.

Best Practices

  1. Erst messen, dann tunen. Erfassen Sie GC-Metriken und Pausen-Logs in Produktion; die Standardeinstellungen passen für die meisten Workloads.
  2. Allokationen auf heißen Pfaden reduzieren, gestützt auf Allokationsprofile und Flame Graphs.
  3. Speicher an den Container anpassen. Die Laufzeit muss ihr Limit kennen: Moderne JVMs und .NET berücksichtigen cgroup-Limits, in Go setzen Sie GOMEMLIMIT etwas unter das Container-Limit.
  4. Den Collector nach Ziel wählen. Latenzkritische Services profitieren auf der JVM von ZGC oder Shenandoah, Batch-Jobs eher von durchsatzorientierten Collectoren.

Typische Fehler

Häufig werden GC-Flags aus Blogartikeln übernommen, ohne zu messen, der Heap wird vergrößert, bis das Problem vorübergehend verschwindet, oder ein Speicherleck wird mit GC-Druck verwechselt. Ein Leck zeigt sich als Heap-nach-GC-Linie, die nie auf ihre Baseline zurückkehrt; GC-Druck als häufige Collections bei stabiler lebender Menge.

Wie Obseria unterstützt

Obseria erfasst GC-Metriken der Laufzeit über OpenTelemetry und den Obseria Agent für JVM, .NET und Go und stellt sie direkt neben die Request-Latenz. Continuous Profiling ergänzt Allokationsprofile, sodass Sie sehen, welcher Code den Müll erzeugt.

Häufig gestellte Fragen

Die meisten Collectoren haben zumindest kurze Stop-the-World-Phasen, deren Länge aber stark variiert. Nebenläufige Collectoren wie ZGC, Shenandoah oder der Go-GC halten Pausen sehr kurz, typischerweise bei etwa einer Millisekunde oder darunter, weil sie den Großteil der Arbeit erledigen, während die Anwendung läuft.

Vergleichen Sie GC-Pausen-Metriken oder GC-Logs mit der Request-Latenz im selben Zeitraum. Fallen p99-Spitzen mit langen Pausen oder einem hohen CPU-Anteil für GC zusammen, trägt der Collector wahrscheinlich bei.

Nein. Der Go-Collector ist nebenläufig, nicht generationell und nicht verschiebend. Gesteuert wird er über GOGC und seit Go 1.19 über GOMEMLIMIT.

Bei einem Leck wächst der nach jeder Collection verbleibende Heap stetig, weil Objekte erreichbar bleiben. Bei GC-Druck ist die lebende Menge stabil, doch eine hohe Allokationsrate erzwingt häufige Collections.

Setzen Sie das Wissen in die Praxis um

Testen Sie Obseria 14 Tage kostenlos, mit OpenTelemetry und ohne proprietäre Agenten.