JEP 523: Make G1 the Default Garbage Collector in All Environments
Introduction
The choice of garbage collector is often the most "invisible" configuration in a Java application. The vast majority of developers never specify it explicitly; they trust the JVM to make a sensible default choice on their behalf. JEP 523 is about the logic behind that default choice, and it replaces a heuristic born two decades ago with a single, uniform, predictable policy: whatever the machine, default to G1.
How the default is picked today
Before Java 27, the JVM did not treat every machine the same way. At startup it would "size up" the host and, based on the machine's specifications, decide which garbage collector to hand you. This logic is known as server-class machine detection:
- The JVM classifies the host as either a "server-class" or a "client-class" machine.
- The threshold is roughly: 2 or more CPUs and 1792 MB or more of memory. Anything meeting that bar is treated as server-class and selects G1.
- Everything below that specification falls back to Serial GC, a single-threaded collector whose pauses grow linearly with heap size.
The root of the problem is that this heuristic dates from an era when single-core desktops were the norm. Back then, the reasoning was sound: only genuine "big machines" (servers) needed a concurrent collector like G1, designed for throughput and low latency, while an ordinary desktop was perfectly well served by Serial GC.
Why the heuristic no longer fits
Cloud-native computing and containerization have fundamentally changed what a "machine" is. Today's "small machine" is no longer an old desktop but one of thousands of container instances running on Kubernetes:
- Containers routinely get just 1 CPU and under 1792 MB of memory, so by the old rule they all end up with Serial GC.
- Two nearly identical deployments can end up on two completely different collectors. Your microservice gets 1 CPU and Serial GC; the almost-identical service next to it gets 2 CPUs and G1. Same code, different collectors.
- Serial GC pauses grow with heap size, which is deadly for latency-sensitive online services; a single long pause can wreck your P99 latency.
- More insidiously, tuning advice, benchmarks, and performance docs often hold true for only one of the two collectors yet get applied indiscriminately to the other. Debugging performance thus becomes a nightmare: you think you are comparing two identical environments, when underneath they run two entirely different memory-management mechanisms.
What JEP 523 changes
The fix is almost anticlimactically simple: regardless of machine specifications, default to G1.
- G1 becomes the default garbage collector in every environment. From a laptop to a one-CPU container to a 64-core server, you get the same collector by default.
- Server-class machine detection no longer selects the collector. That twenty-year-old heuristic is removed from the "which GC do I get" decision chain.
- Serial GC has not gone away. It remains fully supported; it is simply no longer any machine's automatic default. If you genuinely need it, a single
-XX:+UseSerialGCflag brings it back. - G1 itself is unchanged. This is the crucial point: JEP 523 alters only the single decision of "which collector do you get by default." Not a line of the collector implementation was touched.
Trade-offs: what you gain and what to watch
Any change to a default is a trade-off. On balance, G1 is the better default for the vast majority of workloads because its pauses are shorter and more predictable.
What you gain:
- Predictable, lower pause times on small heaps. G1's concurrent, region-based design means pauses stay within a controlled range even as the heap grows.
- The same collector in dev, CI, and production. This eliminates a whole class of "works on my machine" performance discrepancies.
- G1's lower-latency design is available in every environment, no longer reserved for "big machines."
What to watch:
- G1 carries a slightly larger memory footprint. It needs extra data structures to manage its regions, which consume a small amount of memory.
- On tiny, short-lived, single-CPU workloads, Serial GC can still win. For a batch job that runs for a few seconds, Serial's simplicity can be an advantage.
- Measure before you decide. If you are considering switching back to
-XX:+UseSerialGC, base it on real measurements, not intuition.
Conclusion
Starting with Java 27, whether you deploy on a laptop, a one-CPU container, or a 64-core server, you get G1 by default. This change introduces no new features and does not alter G1 itself; it simply fixes a default, yet in doing so it removes a source of hidden inconsistency that has confused the whole ecosystem for years. From now on, the community's accumulated tuning knowledge, benchmark data, and best practices finally apply consistently across every environment.