JVM Internals & Memory

GC Algorithms: Parallel, G1, ZGC, Shenandoah

HotSpot ships a portfolio: Parallel (max throughput, big pauses), G1 (balanced, region-based, the default), ZGC and Shenandoah (concurrent, sub-millisecond pauses at slight throughput cost), and Epsilon (no-op, for testing). Choose by pause-time requirements.
  • G1 (default): heap as ~2048 regions; collects the garbage-first regions within a pause target (-XX:MaxGCPauseMillis, default 200 ms)
  • Parallel: STW everything with all cores — best raw throughput for batch jobs
  • ZGC: colored pointers + load barriers → pauses <1 ms regardless of heap size; generational since JDK 21
  • Shenandoah: brooks-pointer-descended concurrent compaction, similar goals to ZGC
  • Humongous objects (>50% region) stress G1 — watch for them in logs
  • Serial for tiny containers; Epsilon allocates-only (benchmark baseline)
Choosing a collector
CollectorFlagPause profileBest for
G1 (default)-XX:+UseG1GC~10–200 ms, tunable targetgeneral services, balanced
Parallel-XX:+UseParallelGClong STW, all-corebatch/ETL: throughput over latency
ZGC-XX:+UseZGC (+generational, 21+)<1 ms, heap-size independentlatency-critical, large heaps
Shenandoah-XX:+UseShenandoahGC~1 mslatency-critical (Red Hat lineage)
Serial-XX:+UseSerialGClong, single-core<~256 MB heaps, tiny containers
Epsilon-XX:+UseEpsilonGCnone — OOMs when fullbenchmarks, allocation testing

G1's design: dividing the heap into regions decouples "young/old" from contiguous address ranges; the collector tracks per-region liveness and evacuates the emptiest (garbage-first) regions — reclaiming the most space for the least copying — within the pause budget. Remembered sets (per-region incoming-pointer indexes) make regional collection possible without whole-heap scans; concurrent marking keeps liveness data fresh. Mixed collections gradually chew through old-gen garbage; the dreaded fallback full GC (single-threaded until JDK 10, parallel since) signals the steady state failed — usually allocation outrunning marking.

ZGC's trick: metadata bits inside the 64-bit pointer (colored pointers) plus a load barrier on every reference read let it mark and even relocate objects concurrently — a thread touching a moving object gets healed on the fly. Result: max pause under a millisecond on multi-terabyte heaps, in exchange for a few percent throughput and some memory overhead. Generational ZGC (JDK 21) recovered most of the young-collection efficiency it originally gave up.

Sources
  • Optimizing JavaCh. 7 — Advanced Garbage Collection
  • Optimizing Cloud Native Java (2nd ed.)Ch. 5 — Advanced Garbage Collection
  • Java Secrets: High Performance and ScalabilityGC and scalability chapters