Generational ZGC

Java 21 Course · lesson 7 of 15 · 4 min read

ZGC learned the generational trick in 21, cutting its CPU and memory cost sharply.

Open this lesson in the learning hub

Key points

  • ZGC already gave sub-millisecond pauses, but scanned the whole heap every cycle.
  • Most objects die young, so scanning old survivors repeatedly is wasted work.
  • JEP 439 added young and old generations to ZGC, collecting the young part far more often.
  • The result is the same tiny pauses at meaningfully lower CPU and heap overhead.
  • In 21 you opt in with -XX:+UseZGC -XX:+ZGenerational; later releases made it the default.

Example

public class Main {
    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        System.out.println("max heap MB : " + rt.maxMemory() / (1024 * 1024));

        long before = rt.totalMemory() - rt.freeMemory();

        // Churn: almost all of this dies immediately - exactly what a young gen is for
        StringBuilder keep = new StringBuilder();
        for (int i = 0; i < 200_000; i++) {
            String tmp = "garbage-" + i;
            if (i % 50_000 == 0) { keep.append(tmp).append(' '); }
        }

        long after = rt.totalMemory() - rt.freeMemory();
        System.out.println("used before : " + before / 1024 + " KB");
        System.out.println("used after  : " + after / 1024 + " KB");
        System.out.println("survivors   : " + keep);
        System.out.println("GC in use   : " +
            java.lang.management.ManagementFactory.getGarbageCollectorMXBeans()
                .stream().map(b -> b.getName()).toList());
    }
}

Generational ZGC kept the pause guarantee and stopped paying for it with wasted scanning.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Java 21 Course course, and every lesson in it is listed on the Java 21 Course contents page.