한국어English日本語简体中文繁體中文DeutschไทยTiếng ViệtРусскийPortuguês (Brasil)EspañolBahasa Indonesia

Game-Lag-Whitepaper › L10 Arbeitsspeicher

Stop-the-World-GC-Pause auf dem Server Stop-the-world GC pause

Ursachen-ID mem-gc · Hauptzuständig Server-Entwicklung (Entwicklungsteam) · Beteiligt Server-Infrastruktur (Infrastrukturteam)

In der interaktiven Fassung mit Grafiken und Experimenten öffnen →

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

Symptome
Freeze, Zeitraffer
Faktoren
Stillstand
Wer ist betroffen
Ganzer Server
Wann
In festen Abständen, Je länger es läuft
Zuständigkeit
Hauptzuständig Server-Entwicklung (Entwicklungsteam) · Beteiligt Server-Infrastruktur (Infrastrukturteam)
Aufgaben Entwicklungsteam
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.
Reale Fälle
Riot Games 2021: League of Legends EUW, 5-stündiger Ausfall: Eine einzige Neben-DB legt den ganzen Server lahm

Quellen

  1. Garbage-First (G1) Garbage Collector Oracle
    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
  2. JEP 523: Make G1 the Default Garbage Collector in All Environments OpenJDK
    Bisher war bei 1 CPU oder weniger als 1.792 MB Arbeitsspeicher die Serial GC Standard, ab JDK 27 ist überall G1 Standard
  3. JEP 439: Generational ZGC OpenJDK
    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)
  4. JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector (Experimental) OpenJDK
    Shenandoah-Pausenzeiten sind ähnlich, ob der Heap 200 MB oder 200 GB groß ist
  5. 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
  6. A Guide to the Go Garbage Collector Go
    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
  7. JEP 271: Unified GC Logging OpenJDK
    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
  8. The java Command Oracle
    Tabelle zur Umstellung alter GC-Log-Optionen auf -Xlog: aus -XX:+PrintGCDetails wird -Xlog:gc*
  9. 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.)
  10. runtime package Go
    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

Verwandte Ursachen

Gleiche Schicht: L10 Arbeitsspeicher

Ursachen aus anderen Schichten mit demselben Symptom (Freeze)

Karte in der interaktiven Fassung mit Grafiken und Experimenten ansehen