While a Java or C# server halts every thread to collect garbage (stop-the-world), the whole server stalls.
Why The heap fills up and GC starts → Effect Every game thread stops while GC collects (longer the more live data there is) → On screen Everyone on the server freezes at the same moment, then the game fast-forwards
Primary owner Game team (Server development) · Also Infra team (Server infrastructure)
Game team action items
Explicitly select a low-pause GC (ZGC, Shenandoah, or G1 with a lower pause target) in the launch options, reduce allocations, tune heap size.
Infra team action items
Use instances with enough memory for a generous heap, give containers at least 2 CPUs and about 1.8 GB of memory (with less, JDK 26 and earlier pick Serial GC by default), monitor GC pause times.
Ballpark numbers
A minor GC that collects only new objects (the young generation) takes a few to tens of milliseconds. A full GC over a whole heap holding several GB of live data can take over 1 second. ZGC stays under 1 ms almost regardless of heap size, and Shenandoah pauses are also short because they don’t grow with heap size.
On the graph
Periodic spikes · Server tick time, GC pause time
Where to look
Turn on GC logging and overlay pause times and lengths on the server tick-time graph. Java: Pause lines from the startup option -Xlog:gc* (-XX:+PrintGCDetails on JDK 8 and earlier). .NET: the GC pause metric in dotnet-counters (dotnet.gc.pause.time on .NET 9 and later, % Time in GC since last GC on 8 and earlier). Go: the line GODEBUG=gctrace=1 writes for each GC
Confirmed if
Tick spikes line up with GC pauses, and pause length matches spike length. Every zone and channel on the server spikes at the same moment
Ruled out if
Ticks spike with no long pauses in the GC log: another cause such as locks, blocking calls, or disk writes. Only one zone spikes: the script engine’s GC (mem-script-gc) or that zone’s load
Check with
Infra tools (no game code needed)
Learn more
Java’s G1 has a default target of 200 ms per pause, which is 4 ticks on a 20-tick server. If a container gets fewer than 2 CPUs or less than about 1.8 GB of memory, Java on JDK 26 and earlier picks Serial GC, which collects on a single thread, as the default GC, and pauses get much longer. C# (.NET) servers usually turn on server GC and background GC, but collections of generations 0 and 1 (Gen0/1), where new objects live, and full GCs with compaction still stop every thread. Go pauses are usually under 1 ms, but under heavy allocation the code requesting memory has to take on part of the GC work, so ticks slow down. With any GC, if allocation outpaces collection, the game thread eventually stops. G1 falls back to a full GC, and ZGC stalls the thread requesting memory until the collection finishes.
Garbage-First (G1) Garbage CollectorOracle G1 default pause target 200 ms (MaxGCPauseMillis); if memory runs out during collection, it falls back to a full GC that stops and compacts the whole heap
JEP 439: Generational ZGCOpenJDK ZGC pauses are 1 ms or less regardless of heap size; G1 pauses range from a few ms to a few seconds. Risk of allocation stalls when allocation outpaces reclamation
Background garbage collection.NET Background GC applies only to gen2 collections; gen0 and gen1 collections (foreground GC) stop all managed threads
A Guide to the Go Garbage CollectorGo Go’s GC runs mostly concurrently with only short stop-the-world pauses; under heavy allocation, goroutines take on GC work (assist), which adds latency
JEP 271: Unified GC LoggingOpenJDK JDK 9 reimplemented GC logging on unified logging (-Xlog); -Xlog:gc writes one line per GC, like the old -XX:+PrintGC
The java CommandOracle Table mapping old GC log options to -Xlog: -XX:+PrintGCDetails becomes -Xlog:gc*
dotnet-counters diagnostic tool.NET .NET 9 and later show System.Runtime meters (dotnet.gc.pause.time and others); .NET 8 and earlier show the older EventCounters (% Time in GC since last GC and others)
runtime packageGo GODEBUG=gctrace=1: one line per GC with wall-clock time per phase, heap size at GC start and end, and the heap goal