Java 21 Generational ZGC Explained
Introduction
ZGC, production-ready since Java 15Click to read: production-ready since Java 15, keeps garbage collection pauses below a millisecond by doing nearly all its work while the application runs. But the original ZGC had a weakness: it treated every object the same, so each collection had to mark through the whole heap, including long-lived objects which rarely change. With high allocation rates it needed a lot of spare memory and CPU to keep up. Java 21Click to read tutorial: Overview of New Features in Java 21 (JEP 439) made ZGC generational. This tutorial explains generational ZGC: the generational hypothesis behind it, how it works, how to enable it, how it evolved after Java 21, and how it behaves on a runnable example.
The generational hypothesis
Most objects die young. A request handler allocates strings, lists and temporary objects which are garbage moments later, while caches, connection pools and configuration live for the whole run. This observation, the weak generational hypothesis, is why most collectors - including G1, Parallel and Serial - split the heap into a young generation for new objects and an old generation for objects that survive. The young generation is collected often and cheaply, since it is mostly garbage; the old generation is collected rarely. Non-generational ZGC gave up this advantage in exchange for simplicity.
How generational ZGC works
OUTPUT of the above command
Watching minor and major collections
The program below creates many short-lived objects, as a server would, while keeping a long-lived cache in memory - a workload which suits a generational collector - and then reports what ZGC did -
OUTPUT of the above command
Explanation of the output
- The heap is divided into a young and an old generation. New objects are allocated in the young generation, and objects which survive several young collections are promoted to the old one.
- Minor collections collect only the young generation, frequently. Major collections collect both generations, much less often.
- Both kinds of collection are still concurrent: marking, moving and reference fixing run alongside the application, using load barriers and colored pointers, plus new store barriers which track references from old objects to young ones. Pauses stay well below a millisecond.
- Java 21: generational mode was opt-in:
-XX:+UseZGC -XX:+ZGenerational. Plain-XX:+UseZGCstill gave the old, non-generational ZGC. - Java 23 (JEP 474): generational mode became the default for
-XX:+UseZGC, and the non-generational mode was deprecated. - Java 24 (JEP 490): the non-generational mode was removed, so ZGC is always generational and the
ZGenerationaloption is obsolete.
-XX:+UseZGC is all you need, and asking for the old mode only produces a warning -
java -XX:+UseZGC -XX:-ZGenerational -version
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option ZGenerational; support was removed in 24.0 java version "24.0.1" 2025-04-15 Java(TM) SE Runtime Environment (build 24.0.1+9-30) Java HotSpot(TM) 64-Bit Server VM (build 24.0.1+9-30, mixed mode, sharing)
A workload with young and old objects
//GenerationalZgcDemo.java
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GenerationalZgcDemo {
public static void main(String[] args) {
Map<Integer, String> cache = new HashMap<>(); // long-lived objects
for (int i = 0; i < 200_000; i++) cache.put(i, "value-" + i);
long requests = 0, end = System.nanoTime() + 2_000_000_000L;
while (System.nanoTime() < end) { // short-lived objects
List<String> temp = new ArrayList<>();
for (int i = 0; i < 50; i++) temp.add("request-" + requests + "-" + i);
requests++;
}
System.out.printf("Handled %,d requests with a cache of %,d entries%n", requests, cache.size());
for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
System.out.printf(" %-18s count=%-5d time=%d ms%n", gc.getName(), gc.getCollectionCount(), gc.getCollectionTime());
}
}
}
java -XX:+UseZGC -Xmx512m GenerationalZgcDemo
Handled 1,398,222 requests with a cache of 200,000 entries ZGC Minor Cycles count=19 time=51 ms ZGC Minor Pauses count=57 time=1 ms ZGC Major Cycles count=3 time=312 ms ZGC Major Pauses count=15 time=0 ms
- These numbers are from one run on a laptop with a recent JDK, and will vary, but the pattern is typical: there are many more minor cycles than major ones, because the young generation fills up quickly with dead request objects while the cache sits in the old generation.
- The "Cycles" times are concurrent work, done while the application kept running; the "Pauses" are the only time the application was actually stopped, and they add up to very little.
- To see each collection as it happens, add
-Xlog:gc, which logs lines such asGC(3) Minor Collection (Allocation Rate)with their timings.
Continue Reading Java 18 to 21 Tutorials
Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Record Patterns with ExamplesClick to read tutorial: Java 21 Record Patterns with Examples Java 21 Pattern Matching for switch with ExamplesClick to read tutorial: Java 21 Pattern Matching for switch with Examples Java 21 Sequenced Collections ExplainedClick to read tutorial: Java 21 Sequenced Collections Explained Java 21 API Additions: StringBuilder.repeat, Math.clampClick to read tutorial: Java 21 API Additions: StringBuilder.repeat, Math.clamp Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20
Overview of New Features in Java 21Click to read tutorial: Overview of New Features in Java 21 Java 21 Virtual Threads Tutorial with ExamplesClick to read tutorial: Java 21 Virtual Threads Tutorial with Examples Java 21 Record Patterns with ExamplesClick to read tutorial: Java 21 Record Patterns with Examples Java 21 Pattern Matching for switch with ExamplesClick to read tutorial: Java 21 Pattern Matching for switch with Examples Java 21 Sequenced Collections ExplainedClick to read tutorial: Java 21 Sequenced Collections Explained Java 21 API Additions: StringBuilder.repeat, Math.clampClick to read tutorial: Java 21 API Additions: StringBuilder.repeat, Math.clamp Overview of New Features in Java 18Click to read tutorial: Overview of New Features in Java 18 Overview of New Features in Java 19Click to read tutorial: Overview of New Features in Java 19 Overview of New Features in Java 20Click to read tutorial: Overview of New Features in Java 20