Unlock Sub-Millisecond Pauses: Deep Dive into Java’s Z Garbage Collector (ZGC)
ZGC, introduced in JDK 11 and stabilized in JDK 15, is a low‑latency, region‑based, concurrent garbage collector that aims for sub‑millisecond pause times regardless of heap size, leveraging colored pointers, NUMA awareness, multi‑mapping, and read barriers to achieve efficient memory reclamation.
ZGC Overview
Z Garbage Collector (ZGC) is a scalable low‑latency garbage collector introduced in JDK 11 and stabilized in JDK 15. Its design goals are:
< 1ms maximum pause time (JDK < 16: 10 ms, JDK ≥ 16: < 1 ms)
Pause time does not increase with heap, live‑set, or root‑set size
Supports heap sizes from 8 MB up to 16 TB
ZGC has the following characteristics:
Concurrent
Region‑based layout
Compressed
NUMA‑aware
Uses colored pointers
Uses load barriers
Key Features
ZGC is a (temporarily) non‑generational collector built on a region‑based memory layout. It employs read barriers, colored pointers, and multi‑mapping to implement a concurrent mark‑compact algorithm with low latency as the primary goal.
Memory Layout
ZGC does not use generations.
Like Shenandoah and G1, ZGC uses a region‑based heap, but its regions are dynamic: they can be created and destroyed on demand, and their capacity can change. On x64 platforms, regions come in three sizes:
Small Region (2 MB) – stores objects smaller than 256 KB
Medium Region (32 MB) – stores objects between 256 KB and 4 MB
Large Region – size is a multiple of 2 MB and holds objects 4 MB or larger
NUMA‑Aware
NUMA (Non‑Uniform Memory Access) provides each CPU with a local memory region, reducing contention compared to UMA (Uniform Memory Access). ZGC automatically detects and exploits NUMA topology to improve performance on large‑scale servers.
Colored Pointer
Colored pointers store GC metadata directly in the object reference instead of the object header. Each 64‑bit pointer is divided as follows:
18 bits reserved for future use
1 bit – Finalizable flag
1 bit – Remapped flag
1 bit – Marked1 flag
1 bit – Marked0 flag
42 bits – Object address (supports up to 4 TB)
Why two mark bits? Each GC cycle swaps the active mark bit, invalidating the previous cycle’s marks so that all references start as unmarked.
Can ZGC compress pointers? No; ZGC uses 64‑bit pointers, allowing addressing up to 4 TB, far beyond the 32 GB limit of compressed 32‑bit pointers.
Advantages of Colored Pointers
When all live objects in a region are moved, the region can be released immediately because the forward table records old‑to‑new addresses.
Self‑healing pointers reduce the need for write barriers; only a read barrier is required.
18 unused bits provide ample space for future extensions.
Multi‑Mapping Addressing
ZGC uses multi‑mapping on Linux to map several virtual addresses to the same physical memory, allowing the colored pointer’s flag bits to act as address segment selectors. This technique is a by‑product of colored pointers rather than a dedicated feature.
Read Barrier
ZGC uses a read barrier to correct stale references during concurrent relocation. When a thread accesses an object that has been moved, the barrier checks the Remapped flag and redirects the reference to the new location.
<code>Object o = obj.fieldA; // Loading an object reference from heap
<load barrier needed here>
Object p = o; // No barrier, not a load from heap
o.doSomething(); // No barrier, not a load from heap
int i = obj.fieldB; // No barrier, not an object reference</code>The presence of load barriers adds roughly a 4 % overhead to application throughput.
ZGC Work Process
ZGC operates in four main concurrent phases:
Concurrent Mark : Traverses the object graph to determine reachability, updating the Marked0/Marked1 bits in colored pointers.
Concurrent Prepare for Relocate : Selects Regions to be relocated (the Relocation Set) based on heuristics.
Concurrent Relocate : Copies live objects to new Regions, builds a forward table, and uses read barriers to self‑heal stale references.
Concurrent Remap : Fixes remaining stale references; this work is merged into the next marking phase to avoid an extra traversal.
Core Parameters
-XX:+UseZGC – Enable ZGC
-Xmx – Set maximum heap size
-Xlog:gc – Enable GC logging
-Xlog:gc* – Enable detailed GC logging
GC Trigger Conditions
Timer‑based : Configurable via ZCollectionInterval
Warm‑up : Triggers at 10 %, 20 %, 30 % heap usage (up to three times)
Allocation Rate : Predicts when the heap will be exhausted based on recent allocation speed
Proactive : Triggers if heap growth exceeds 10 % within 5 minutes
Metadata Allocation : Triggers when metadata space is low
Explicit : System.gc() call
Allocation Stall : Threads block because the heap is full
ZGC Log Analysis Example
The following Java program runs with
-XX:+UseZGC -Xmx8m -Xlog:gc*on JDK 17. The log shows phases such as Start, Phase‑Pause Mark Start/End, Phase‑Pause Relocate Start, heap size changes, and statistical summaries.
<code>/**
* VM Args:-XX:+UseZGC -Xmx8m -Xlog:gc*
*/
public class HeapOOM {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[2048]);
}
}
}
</code>Conclusion
The article described ZGC’s concepts, features, and workflow.
Most companies still use JDK 8/11 with ParNew+CMS or G1 collectors.
Java developers should stay curious about new technologies like ZGC to maintain a competitive edge.
References
Deep Understanding of the JVM, 3rd Edition – Zhou Zhimin
https://wiki.openjdk.java.net/display/zgc/Main
http://cr.openjdk.java.net/~pliden/slides/ZGC-Jfokus-2018.pdf
https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.