Garbage Collectors: Generational G1 vs ZGC Colored Pointers
- Last updated
- Prerequisites:
- JVM memory model basics (heap, regions)
- Understanding of GC concepts (mark, sweep, compact)
- java
- garbage-collection
- g1
- zgc
- shenandoah
- jvm
- memory
Read at your depth
The practical view
G1 is the default GC since JDK 9: it divides the heap into regions and collects incrementally, prioritizing regions with the most garbage (garbage-first). Use -XX:+UseG1GC and tune with -XX:MaxGCPauseMillis. ZGC (-XX:+UseZGC) is a low-latency collector designed for very large heaps (up to terabytes): it keeps pause times under ~1ms regardless of heap size by doing most work concurrently with the application. Shenandoah (-XX:+UseShenandoahGC) is similar (OpenJDK, also in Red Hat builds). Choose G1 for general throughput, ZGC/Shenandoah for latency-critical services with large heaps.
The same idea in other frameworks
N/A — JVM runtime concern.N/A — server-side JVM concern.N/A — JVM concern.N/A — JVM concern.Legacy vs modern
G1 default config vs ZGC low-latency config
G1 targets bounded pauses with default settings; ZGC trades CPU/memory for near-zero pauses on large heaps.
java -Xms4g -Xmx4g -jar app.jar # ❌ OLD: default G1, pauses can spike on large heapsjava -XX:+UseZGC -Xms16g -Xmx16g -jar app.jar # ✅ NEW: sub-ms pauses at any heap sizeInterview gotchas
Context
This separates candidates who understand the load-barrier/colored-pointer design from those who just know 'ZGC is low latency' — interviewers want the mechanism that removes heap-size dependency.
The mechanical answer
ZGC's pause time is bounded by the number of GC roots (thread stacks, registers, JNI roots), not by heap size, because all the heap-wide work happens concurrently with the application. The mechanism is the load barrier + colored pointers: the GC encodes per-region state (marked0/marked1/remapped/finalizable) in the unused high bits of 64-bit object references. When application threads load an object reference, the load barrier checks the color bits and, if the region is being remapped/relocated, adjusts the pointer (or reads the forwarding address) on the spot. Marking and relocation are therefore incremental and concurrent — the app threads cooperate through barriers rather than stopping. G1, by contrast, performs evacuation with a stop-the-world pause whose duration grows with the live-set size being copied and the remembered-set scanning work.
Trap
The naive answer is 'ZGC uses more CPU so it's faster'. The real distinction is the concurrency model: barrier-assisted concurrent relocation vs stop-the-world evacuation. Another trap: thinking ZGC works on any heap size for free — it needs enough memory for the multi-mapped/forwarding overhead, and generational ZGC is recommended for typical workloads to reduce allocation-rate overhead. Candidates who mention colored pointers and load barriers show real understanding.
Context
This probes G1's incremental collection model — interviewers want the region-based garbage-first heuristic and the cross-region reference tracking that makes partial collections correct.
The mechanical answer
G1 divides the heap into equal-sized regions and maintains a global marking (concurrent) that identifies live objects. The 'garbage-first' heuristic ranks regions by the ratio of garbage to live data and selects a set of regions (the collection set, CSet) for evacuation each cycle, aiming to reclaim the most memory within the pause-time goal (-XX:MaxGCPauseMillis). Remembered sets (RSets) track which regions contain references pointing INTO each region; when evacuating a region, G1 scans its RSet to find and update all incoming references from other regions. SATB (snapshot-at-the-beginning) barriers ensure concurrent marking captures a consistent snapshot. Young collections promote survivors; mixed collections evacuate old regions.
Trap
The naive answer is 'G1 compacts the whole heap'. It evacuates a subset of regions per collection, so the heap is never fully compacted at once (fragmentation can still occur in old regions). Another trap: assuming RSets are free — they consume memory and update CPU, which is why humongous objects (larger than half a region) are allocated specially and can cause premature full GCs. Candidates who describe CSet selection, RSets, and SATB show real G1 understanding.