JVM Internals & Memory
GC Algorithms: Parallel, G1, ZGC, Shenandoah
- 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)
| Collector | Flag | Pause profile | Best for |
|---|---|---|---|
| G1 (default) | -XX:+UseG1GC | ~10–200 ms, tunable target | general services, balanced |
| Parallel | -XX:+UseParallelGC | long STW, all-core | batch/ETL: throughput over latency |
| ZGC | -XX:+UseZGC (+generational, 21+) | <1 ms, heap-size independent | latency-critical, large heaps |
| Shenandoah | -XX:+UseShenandoahGC | ~1 ms | latency-critical (Red Hat lineage) |
| Serial | -XX:+UseSerialGC | long, single-core | <~256 MB heaps, tiny containers |
| Epsilon | -XX:+UseEpsilonGC | none — OOMs when full | benchmarks, 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.