# ZGC — Generational, Colored-Pointer GC (OpenJDK)

## §1 Provenance

- **OpenJDK Z Garbage Collector.** Engineering lead: Per Liden (originally), Stefan Karlsson, Erik Österlund at Oracle.
- Key JEPs:
  - **JEP 333** (2018) — original experimental ZGC.
  - **JEP 377** (JDK 15, 2020) — production-ready.
  - **JEP 439** (JDK 21, Sep 2023) — Generational ZGC, opt-in.
  - **JEP 474** (JDK 23, 2024) — Generational mode by default; non-generational mode deprecated.
  - JDK 25 (Sep 2025, LTS) — generational-only ZGC.
- Project home: https://openjdk.org/projects/zgc/, wiki https://wiki.openjdk.org/spaces/zgc/pages/34668579/Main.

## §2 Mechanism

ZGC is a **concurrent, region-based, compacting** collector. Its defining trick is **colored object pointers** ("colored oops"):

- A heap pointer is 64-bit. The low 42 bits hold the address (so heaps up to 4 TB). The middle 4 bits hold metadata colors: `Finalized`, `Remapped`, `Marked0`, `Marked1`. The high 18 bits are reserved.
- A **load barrier** runs on every reference load from the heap into a register. It checks the color bits against the current GC phase. On mismatch, it "heals" the pointer: it updates the color, and (during relocation) re-resolves the address through a forwarding table. The application thread does the work in microseconds and proceeds.
- Marking, relocation, and remapping all run **concurrently** with mutators. The only stop-the-world points are very short root-scanning and phase-transition handshakes — typically well under 1 ms.

**Generational ZGC** (JEP 439, default since JDK 23) adds:
- A young-generation heap with frequent collections.
- An old-generation heap collected less often.
- New color bits so a **store barrier** on every reference write can record cross-generational pointers without a separate card table.
- The empirical payoff (Apache Cassandra workload, per JEP 439): **¼ the heap, 4× the throughput** of non-generational ZGC, while keeping pauses under 1 ms.

The "colored pointer" idea predates ZGC (Azul C4) but JEP 333/439 is the version every other JVM team studies.

## §3 Memory-safety property

ZGC is a tracing GC, so it provides full **temporal safety and reachability-precise reclamation** for Java's managed heap. There is no UAF; objects survive as long as they are reachable. Spatial safety comes from the JVM's bytecode verifier and bounds checks, not from ZGC. Concurrency safety with mutators is enforced by the load barrier (no thread can see a stale relocation), making moving GC safe under JIT optimisation.

## §4 Production status (May 2026)

- Generational ZGC has been the **default ZGC** since JDK 23 (Oct 2024). Since JDK 25 (Sep 2025, the current LTS), non-generational ZGC has been removed.
- ZGC is the production choice for low-latency services (Netflix, Twitter/X historically, Cassandra deployments). It still trails G1 in mean throughput for batch workloads, but wins decisively when p99 latency matters.
- Wikipedia and OpenJDK confirm heap sizes from 8 MB to 16 TB are supported.

Measurements (JEP 439, JDK 21):
- Cassandra: 4× throughput, ¼ heap vs non-generational ZGC.
- SPECjbb2015: ~10% throughput delta vs Generational G1; ZGC wins on p99.
- Pause times: typically 0.1-0.5 ms, max under 1 ms on multi-GB heaps.

## §5 Cost

- **Throughput.** 5-15% lower than G1 on batch workloads (the cost of the load barrier). Generational ZGC closed most of this gap.
- **Memory.** Higher than G1: the colored-pointer scheme reserves virtual address space generously and uses multi-mapping (the same physical page mapped at three virtual addresses for the three color states). Typical 5-10% RAM overhead vs G1.
- **Latency.** The headline win. Sub-millisecond max pause times, in practice often microseconds.
- **Cache footprint.** Generational design ameliorated the "always-tracing the whole heap" problem of single-gen ZGC.

## §6 Mochi adaptation note

ZGC is way too heavy for vm3 directly — colored pointers require multi-mapping, hardware atomic-CAS on every pointer load, and a JIT smart enough to elide barriers. Mochi cannot adopt it. But several **lessons** apply:

1. **Color bits in the Cell (MEP-40 §6.1).** The vm3 Cell already has 4 bits for the arena tag and 12 bits for the generation. Reserve, say, 2 of the generation bits as a **mark color** during a concurrent sweep cycle. The mutator's reads need no barrier (since we never *move* a slab entry — only reclaim it), but the sweeper can run concurrently if writes to the color bits are atomic. This is the smallest patch that captures the "concurrent mark/sweep" win.
2. **Generational discipline.** Even without a moving collector, partition each per-type arena (MEP-40 §6.2) into a "young" slab range and an "old" slab range. Free-list reuse is biased to young slabs; the mark-sweep cycle (Phase 6, §9.2) walks young more often than old. This is the LXR / ZGC generational insight in cheaper form.
3. **No load barrier needed.** Mochi's huge advantage over JVM is that handles don't move. The Go runtime owns the backing slices, so a slab entry's identity (handle) is stable. No barrier on every read.

This does **not** conflict with Mochi's design ethos: we are using a single ZGC *insight* (concurrent color marking) without inheriting any of ZGC's hardware demands or cgo.

## §7 Open questions for MEP-41

- How much concurrency in the sweep step is worth the complexity, given that Mochi today is single-threaded? Phase 6 of MEP-40 plans a stop-the-world mark-sweep over arenas; concurrent marking buys little if pauses are already sub-ms.
- Does generational partitioning of slabs measurably reduce mark-phase work on the Mochi corpus? Need a sim before committing.
- ZGC's recent direction is to amortise read barriers across the JIT (Crochemore-style elision). vm3jit (MEP-40 Phase 5) will face the same question: how much barrier work can the JIT remove?
- What's the right number of color bits? ZGC uses 4 (Finalized/Remapped/Marked0/Marked1); a non-moving collector probably needs only 1-2.

## Sources

- [JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)](https://openjdk.org/jeps/333)
- [JEP 439: Generational ZGC](https://openjdk.org/jeps/439)
- [JEP 474: ZGC: Generational Mode by Default](https://openjdk.org/jeps/474)
- [OpenJDK ZGC project](https://openjdk.org/projects/zgc/)
- [ZGC Wiki](https://wiki.openjdk.org/spaces/zgc/pages/34668579/Main)
- [Pauseless GC in Java 25: ZGC Deep Dive](https://andrewbaker.ninja/2025/12/03/pauseless-garbage-collection-in-java-25-zgc-guide/)
