Während ein Java- oder C#-Server alle Threads anhält, um Garbage einzusammeln (Stop-the-World), steht der gesamte Server still.
Warum Heap voll, GC startet → Folge Alle Game-Threads angehalten, während die GC sammelt (je mehr lebende Daten, desto länger) → Auf dem Bildschirm Alle auf dem Server stehen gleichzeitig still, danach Zeitraffer
GC mit kurzen Pausen (ZGC, Shenandoah, G1 mit niedrigerem Pausenziel) per Startoption explizit festlegen, Allokationen reduzieren, Heap-Größe anpassen.
Aufgaben Infrastrukturteam
Instanzen mit genug Arbeitsspeicher für einen großzügigen Heap wählen, Containern mindestens 2 CPUs und mindestens ca. 1,8 GB Arbeitsspeicher zuweisen (darunter wählt Java bis JDK 26 standardmäßig die Serial GC), GC-Pausenzeiten überwachen.
Größenordnungen
Eine Minor GC, die nur neue Objekte (Young Generation) sammelt, dauert im ein- bis zweistelligen Millisekundenbereich. Eine Full GC über den gesamten Heap mit mehreren GB lebender Daten kann länger als 1 s dauern. ZGC bleibt fast unabhängig von der Heap-Größe unter 1 ms, und auch bei Shenandoah sind die Pausen kurz, weil sie nicht mit der Heap-Größe wachsen.
Im Graphen
Spitzen in festen Abständen · Server-Tick-Zeit, GC-Pausenzeit
Wo nachsehen
GC-Log aktivieren und Zeitpunkt und Dauer der Pausen über den Graphen der Server-Tick-Zeit legen. Java: Pause-Zeilen der Startoption -Xlog:gc* (bis JDK 8: -XX:+PrintGCDetails), .NET: GC-Pausenmetrik in dotnet-counters (ab .NET 9 dotnet.gc.pause.time, bis 8 % Time in GC since last GC), Go: die Zeile, die GODEBUG=gctrace=1 pro GC schreibt
Spricht dafür
Tick-Spitzen und GC-Pausen fallen zeitlich zusammen, Pausendauer etwa gleich Spitzendauer. Alle Zonen und Kanäle des Servers schlagen im selben Moment aus
Spricht dagegen
Keine langen Pausen im GC-Log, Tick schlägt trotzdem aus: andere Ursache wie Locks, synchrone Aufrufe oder Schreibzugriffe auf den Datenträger. Nur eine Zone schlägt aus: GC der Skript-Engine (mem-script-gc) oder Last in dieser Zone
Prüfmittel
Mit Infrastruktur-Tools prüfbar (ohne Spielcode)
Mehr dazu
Bei Java liegt das Standard-Pausenziel von G1 bei 200 ms pro Pause. Auf einem Server mit 20 Ticks pro Sekunde sind das 4 Ticks. Bekommt ein Container weniger als 2 CPUs oder weniger als etwa 1,8 GB Arbeitsspeicher, wählt Java bis JDK 26 die Serial GC als Standard. Sie sammelt mit einem einzigen Thread, und die Pausen werden deutlich länger. C#-Server (.NET) schalten meist Server-GC und Background-GC ein. Die Sammlung der Generationen 0 und 1 (Gen0/1), in denen neue Objekte liegen, und eine Full GC mit Kompaktierung halten trotzdem alle Threads an. Bei Go liegen die Pausen meist unter 1 ms. Bei vielen Allokationen muss aber der Code, der Speicher anfordert, einen Teil der GC-Arbeit übernehmen, und der Tick wird langsamer. Für jedes Verfahren gilt: Wird schneller alloziert als gesammelt, bleibt am Ende der Game-Thread stehen. G1 fällt dann auf eine Full GC zurück, ZGC hält den anfordernden Thread an, bis die Sammlung fertig ist.
Garbage-First (G1) Garbage CollectorOracle Standard-Pausenziel von G1: 200 ms (MaxGCPauseMillis). Geht während der Sammlung der Speicher aus, Rückfall auf eine Full GC, die alles anhält und den gesamten Heap kompaktiert
JEP 439: Generational ZGCOpenJDK ZGC-Pausen höchstens 1 ms und unabhängig von der Heap-Größe, G1-Pausen einige ms bis einige Sekunden. Wird schneller alloziert als freigegeben, droht ein Allocation Stall (Anhalten bei der Allokation)
Background garbage collection.NET Background-GC gilt nur für Sammlungen der Generation 2, Sammlungen der Generationen 0 und 1 (Foreground-GC) halten alle verwalteten Threads an
A Guide to the Go Garbage CollectorGo Die Go-GC läuft größtenteils nebenläufig und hat nur kurze Stop-the-World-Pausen, bei vielen Allokationen übernehmen Goroutinen GC-Arbeit (Assist), was zu Verzögerungen führt
JEP 271: Unified GC LoggingOpenJDK Ab JDK 9 GC-Logging auf Basis von Unified Logging (-Xlog) neu implementiert, -Xlog:gc schreibt wie früher -XX:+PrintGC eine Zeile pro GC
The java CommandOracle Tabelle zur Umstellung alter GC-Log-Optionen auf -Xlog: aus -XX:+PrintGCDetails wird -Xlog:gc*
dotnet-counters diagnostic tool.NET Ab .NET 9 Anzeige über System.Runtime-Meter (dotnet.gc.pause.time u. a.), bis .NET 8 über die älteren EventCounter (% Time in GC since last GC u. a.)
runtime packageGo GODEBUG=gctrace=1: eine Zeile pro GC mit Wall-Clock-Zeit pro Phase, Heap-Größe zu Beginn und Ende der GC sowie Ziel-Heap