by Arpit Kumar
08 Jun, 2023
7 minute read
Demystifying ZGC: Concurrent Garbage Collection and Colored Pointers

With Java 21 now Generational ZGC is production ready. It still is not the default which is G1GC, but it is a development worth exploring again


Recently I read that Java is making a preview feature for making it easy to learn for new developers. This features will let developer use simplified version without the whole boilerplate of writing the king of boilerplate code

But this is not what got me interested in Java 21. With Java 21 now Generational ZGC is production ready. It still is not the default which is G1GC, but it is a development worth exploring again.


Hello World Java 21
source - (https://twitter.com/fireship_dev/status/1665414702942363654/photo/1)

Introduction to ZGC

First of all Z doesn’t stand for anything. It is a homage to the ZFS file system which is a highly performant filesystem.

In the world of Java, garbage collection plays a crucial role in managing memory and ensuring the efficient execution of applications. With the ever-increasing demands of modern software, there is a growing need for garbage collectors that can deliver both high scalability and low pause times. This is where the Z Garbage Collector, commonly known as ZGC, emerges as a game-changer. We will explore the key features and benefits of ZGC, as well as its journey from an experimental feature to a production-ready solution.

  • Concurrent and Scalable: At its core, ZGC is a concurrent garbage collector, designed to minimize the impact on application response time. Unlike traditional stop-the-world garbage collectors, ZGC allows heavy lifting work to be performed while Java threads continue executing. This concurrent approach significantly reduces pause times and ensures smooth and responsive application behavior.
  • Region-Based: ZGC operates on the concept of regions, dividing the heap into a set of fixed-size memory blocks. This region-based approach allows ZGC to focus its efforts on specific memory areas, optimizing its operations.
  • Relocation: ZGC employs a technique called “colored pointers” to relocate objects in memory while the application is running. During the marking phase, ZGC identifies live objects and copies them to a new location in memory. It updates the references to these objects with colored pointers, ensuring that the application threads can still access them correctly. By relocating objects concurrently, ZGC minimizes the time required for relocation and further reduces pause times.
  • Sub-Millisecond Pause Times: One of the most remarkable aspects of ZGC is its ability to achieve sub-millisecond pause times. This means that even in large heaps ranging from 8MB to a massive 16TB, the time taken for garbage collection pauses remains consistently low. This predictability ensures that applications can maintain high throughput and responsiveness, even in memory-intensive scenarios.
  • No Dependency on Heap, Live-Set, or Root-Set Size: Another notable advantage of ZGC is that its pause times are not affected by the size of the heap, live-set (active objects in memory), or root-set (objects directly referenced by application threads). This characteristic ensures that ZGC can handle varying workloads without compromising its latency goals.
  • NUMA-Awareness and Colored Pointers: ZGC is designed to be NUMA-aware, which means it takes into account the Non-Uniform Memory Access architecture commonly found in modern servers. By understanding the memory layout of the system, ZGC optimizes memory allocation and collection operations for improved performance. Additionally, ZGC utilizes colored pointers, a technique that enables efficient tracking of object references during garbage collection, further enhancing its overall efficiency. We will talk about colored pointers later.
  • Load Barriers: ZGC incorporates load barriers, a mechanism that ensures correct memory ordering and synchronization between the garbage collector and application threads. Load barriers allow ZGC to maintain data consistency during concurrent garbage collection, eliminating potential issues such as stale or incorrect object references.
  • From Experimental to Production Ready: ZGC made its debut as an experimental feature in JDK 11, offering developers a glimpse into its capabilities. Over time, through rigorous testing and feedback from the Java community, ZGC evolved into a mature and reliable solution. As a testament to its stability and effectiveness, ZGC was officially declared production-ready in JDK 15, opening doors for widespread adoption and utilization in various Java applications.

The Z Garbage Collector (ZGC) represents a significant advancement in garbage collection technology for the Java platform. Its ability to deliver sub-millisecond pause times, scalability, and concurrency make it an excellent choice for applications with large heaps and stringent latency requirements. By seamlessly integrating into the Java ecosystem and providing predictable performance, ZGC empowers developers to create highly responsive and efficient applications. As the demand for high-performance garbage collectors continues to grow, ZGC stands out as a reliable and powerful solution, driving Java applications towards even greater levels of productivity and performance.


Java’s Highly Scalable Low-Latency Garbage Collector : ZGC
Source - [Java’s Highly Scalable Low-Latency Garbage Collector : ZGC](https://www.youtube.com/watch?v=U2Sx5lU0KM8)

How Colored Pointers help in efficient GC

In traditional compacting garbage collectors, objects are typically moved in place during the compaction phase. This means that objects are shifted within the heap to create a contiguous block of free memory. Moving objects in place requires updating all references to the moved objects, both within the heap and potentially in the application code. This process can be complex and time-consuming, resulting in longer pause times.

As we already know now, ZGC divides the heap into fixed-size regions. Each region is considered an independent allocation unit and maintains its own allocation information.

When a region becomes full, ZGC selects live objects from that region and copies them to a new empty region. The copying process is performed concurrently with the application threads.

As objects are copied to the new region, their original references in the heap are updated with colored pointers. These pointers redirect references from the original location to the new location of the object. The colored pointers ensure that application threads can still access the objects correctly, even though they have been physically relocated.

With colored pointers, ZGC can easily update references to relocated objects. Instead of traversing the entire heap to update references, ZGC only needs to update references that point to objects being copied.

The copying process is performed concurrently with the application threads, meaning the application’s execution is not halted for an extended period. This contributes to ZGC’s low pause time goal.

When an application thread encounters a reference to a relocated object, it follows the colored pointer to access the object’s new location. This redirection allows the application to seamlessly access the copied object without being aware of its relocation.

Once all references have been resolved and updated with colored pointers, the original region that contained the copied objects becomes empty and available for reuse. During subsequent garbage collection cycles, ZGC can identify and reclaim these empty regions efficiently.

ZGC’s copying approach involves selecting live objects from a full region and copying them to a new region while updating references with colored pointers. This avoids the need for expensive in-place object movement and reduces fragmentation, contributing to shorter pause times and efficient memory management.

Colored pointers in ZGC are stored in a separate data structure to facilitate the redirection of references from the original memory location to the new location of objects. This allows ZGC to update references and access the copied objects correctly while still maintaining the fixed-size allocation units (regions) for efficient memory management.

This ZGC is single generation GC which means it scans the whole heap in the mark phase.

Implementation of Colored Pointers and Load Barrier for the win

ZGC is designed to exclusively support 64-bit architectures. In such architectures, pointers typically utilize 48 bits for addressing, leaving 16 unused bits that can be utilized for storing additional meta information.


Java Colored Pointer

Only one of the remapped, marked 1 or marked 0 can be 1 at a time. Remapped indicates the reference object has been relocated.

The load barrier in ZGC inserts processing logic for pointers whenever they are loaded.

If the pointer points to a relocated object, the load barrier corrects the pointer. During the marking phase, if the pointer is unmarked, the load barrier marks the pointer. During the relocating phase, if the pointer points to regions where objects will be relocated, the object indicated by the pointer is relocated, and then the pointer is corrected.

The load barrier ensures that every time a pointer is loaded, whether by GC threads or Java threads, it correctly accesses the intended object.

Generational ZGC - JEP 439

As already discussed, ZGC was single generational until now. Now after the JEP 439, it brings generational ZGC to the table.

This will further enhance application performance by extending the Z Garbage Collector (ZGC) with separate generations for young and old objects. This improvement enables more frequent collection of young objects, which tend to have shorter lifespans.

It reduces the risk of allocation stalls, lower heap memory overhead and decreased CPU overhead for garbage collection. These benefits should be achieved without a significant decrease in throughput compared to non-generational ZGC. Moreover, the key features of non-generational ZGC should be preserved. Pause times should not exceed 1 millisecond. Support for heap sizes ranging from a few hundred megabytes to several terabytes.

Ultimately, Generational ZGC is intended to be a superior solution for most use cases compared to non-generational ZGC. The goal is to eventually replace the latter with Generational ZGC to reduce long-term maintenance costs.


Reference Material

Recent Posts

Understanding Asynchronous I/O in Linux - io_uring
Explore the evolution of I/O multiplexing from `select(2)` to `epoll(7)`, culminating in the advanced io_uring framework
Building a Rate Limiter or RPM based Throttler for your API/worker
Building a simple rate limiter / throttler based on GCRA algorithm and redis script
MicroVMs, Isolates, Wasm, gVisor: A New Era of Virtualization
Exploring the evolution and nuances of serverless architectures, focusing on the emergence of MicroVMs as a solution for isolation, security, and agility. We will discuss the differences between containers and MicroVMs, their use cases in serverless setups, and highlights notable MicroVM implementations by various companies. Focusing on FirecrackerVM, V8 isolates, wasmruntime and gVisor.

Get the "Sum of bytes" newsletter in your inbox
No spam. Just the interesting tech which makes scale possible.