Java 35: GC Algorithms — G1, ZGC, Shenandoah
easy⏱ 5 mincoursejava
Serial, Parallel, G1, ZGC, Shenandoah
Serial (-XX:+UseSerialGC): single-threaded, for small heaps/embedded. Parallel (-XX:+UseParallelGC): multi-threaded, throughput-first, long pauses. G1 (-XX:+UseG1GC): region-based, predictable pauses, default since Java 9. ZGC (-XX:+UseZGC): concurrent, pauses < 10ms. Shenandoah (-XX:+UseShenandoahGC): Red Hat's concurrent GC, similar goals to ZGC.
G1 — Garbage First
Divides heap into ~2048 regions (1-32MB each). Concurrent Marking identifies the regions with the most garbage — G1 collects those first (hence the name). Minor GC (young regions only) and mixed GC (young + some old) keep pauses predictable. Tune with -XX:MaxGCPauseMillis=200.
ZGC & Shenandoah — concurrent evacuation
Both move objects while the app runs — no stop-the-world evacuation. Technique: load barriers (ZGC) or Brooks pointers (Shenandoah) — every reference read goes through a check that may heal to the new location. Pause times ~1ms regardless of heap size. Cost: ~15% throughput vs G1.
Build a GC picker
Given a workload { heapGB, latencyTarget, throughputPriority }, recommend G1, ZGC, Shenandoah, Parallel, or Serial. Rules: latency < 10ms → ZGC/Shenandoah. Heap < 200MB → Serial or Parallel. Heap > 16GB → ZGC. Default → G1. Test with 5 realistic scenarios.
Always enable GC logging in prod
-Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=20M logs every GC event. Tools like GCViewer or gceasy.io parse these for you. If you don't have this flag in prod, you're flying blind on memory issues.
Quiz: Why G1 pauses are predictable
How does G1 give you MaxGCPauseMillis=200? It collects only as many regions as it can finish in the budget. It estimates per-region copy cost from recent GCs and picks the top-K garbage-densest regions that fit the time budget. Trade: if garbage exceeds what fits in one cycle, Old grows — eventually forcing a full GC.