Your application may be running with a different GC than you think.

In Java 25, G1 is the default garbage collector. But the JVM offers others, each one for a different need. And on a small machine, it picks another one on its own.

The GCs available in Java 25

GCWhat Oracle’s guide saysOption
SerialOne thread does all the GC work. For a small data set (up to about 100 MB) or a single processor-XX:+UseSerialGC
ParallelThe “throughput collector”: like Serial, but with several threads. When raw performance comes first and pauses of a second or more are fine-XX:+UseParallelGC
G1A “mostly concurrent” collector: it does part of its work while the application runs. When response time matters more than throughput-XX:+UseG1GC
ZGCPauses under a millisecond, at the cost of some throughput. When low latency is the priority-XX:+UseZGC
ShenandoahAnother low-pause GC. Oracle’s guide does not cover it; it works on Temurin 25-XX:+UseShenandoahGC

And a special one: Epsilon. It allocates memory but never frees it. When the heap is full, the JVM stops. It is meant for tests (measuring an application without any GC, checking that a test does not create too many objects) or very short jobs. In Java 25, it is still experimental: you need -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC.

The default GC: G1… except on a small machine

The JVM picks G1 on a “server-class” machine: at least two processors and at least 1792 MB of physical memory. Otherwise, it picks Serial.

On a developer machine, it is almost always G1. But in a small container limited to one CPU, the JVM silently switches to Serial. So your application can behave differently on your laptop and in production.

Oracle’s advice: unless you have strict pause-time requirements, run your application first and let the JVM choose.

How to check which GC is running

At startup, with -Xlog:gc, the JVM tells you on the first line:

[0.015s][info][gc] Using G1

Without starting your application, -XX:+PrintFlagsFinal shows what the JVM picks on this machine (ergonomic means “chosen by the JVM”):

     bool UseG1GC                                  = true                                      {product} {ergonomic}

And for an application that is already running, jcmd <pid> VM.flags lists its options, for example -XX:+UseG1GC.

What has changed

Timeline of Java garbage collectors from Java 8 to Java 25: G1 by default in Java 9, CMS removed in Java 14, ZGC and Shenandoah ready for production in Java 15, then their generational modesJava GCs from Java 8 to Java 25Java 8Parallel by defaultPermGen → MetaspaceJava 9G1 by defaultCMS deprecatedJava 11ZGC, Epsilon(experimental)Java 12Shenandoah(experimental)Java 14CMS removedJava 15ZGC, Shenandoahfor productionJava 21Generational ZGC(option)Java 23Generational ZGCby defaultJava 24ZGC: generationalonlyJava 25GenerationalShenandoah stable
What you still read onlineWhat is true todaySince
Parallel is the default GCG1 is the default GCJava 9
CMS is the low-pause GCCMS is gone. On Java 17, 21 and 25, -XX:+UseConcMarkSweepGC even stops the JVM from startingDeprecated in Java 9, removed in Java 14
ZGC and Shenandoah are experimentalThey are ready for productionJava 15
ZGC has no generationsZGC is generational, and that is the only mode since Java 24Java 21 to 24
Shenandoah has no generationsIts generational mode is stable in Java 25, but it is not the default (-XX:ShenandoahGCMode=generational)Java 25

Sources: Oracle’s garbage collection guide (JDK 25), JEP 248 (G1 by default), JEP 291 and JEP 363 (CMS), JEP 377 (ZGC), JEP 379 and JEP 521 (Shenandoah), JEP 318 (Epsilon).

Summary

G1 is a good place to start, and it is what the JVM picks on a normal machine. ZGC and Shenandoah aim for the shortest pauses, Parallel for throughput, Serial for small machines. Before you switch GCs, check which one is really running, especially in a container.

Further reading