Interview Prep Zoneby CuriouserLabs

Question 3 of 5 · architect level

G1, ZGC, Parallel, Serial — how do you choose and tune a garbage collector for a service?

🎤 Say this first

Choose by SLO, not fashion: Parallel maximizes throughput and is fine for batch; G1 (default) balances pause vs throughput for typical services; ZGC (generational since JDK 21) delivers sub-millisecond pauses for latency-critical or very large heaps, at some throughput/memory cost. Tune by setting a pause goal and heap size first, measuring with GC logs/JFR — not by copying flag lists off blogs.

The full picture

CollectorDesign goalPause profilePick when
SerialSmallest footprintLong, single-threadedTiny containers, CLI tools
ParallelMax throughputLong but efficient STWBatch/ETL where p99 latency is irrelevant
G1 (default)Balanced, region-basedTarget via MaxGCPauseMillis (~200ms default)Most services, heaps 4–32GB
ZGC (generational)Ultra-low latency, concurrent<1ms typical, heap-size independentTrading, gateways, multi-100GB heaps
ShenandoahLow latency (Red Hat lineage)Few ms, concurrent compactionSimilar niche to ZGC

A sane tuning workflow: (1) fix the heap size — in containers via MaxRAMPercentage; (2) pick the collector by SLO; (3) enable -Xlog:gc* and observe allocation rate, promotion rate, pause distribution; (4) only then touch advanced flags. 90% of 'GC problems' are actually allocation-rate problems or heap-sized-too-small problems — fixing the code (object churn, caching giant graphs) beats flag alchemy.

🔄 Likely follow-up questions

  • What GC metrics would you put on a dashboard? (pause p99, allocation rate, promotion rate, GC CPU %)
  • What changed with generational ZGC in JDK 21?
  • How would GC choice differ for a 500MB microservice vs a 200GB cache node?