You still read a lot of advice about the garbage collector. Some of it was wrong from the start, and some of it is not true anymore.

Here are five common myths, and what the official documentation and a real JVM say in Java 25.

“System.gc() forces a GC”

No: it is a request, not an order. The javadoc says that System.gc() “suggests” that the JVM frees memory, with no guarantee about how much is freed, or even that the work is done at all.

In practice, on Java 25, the request is often followed, and it is expensive:

GCWhat System.gc() does
G1 (default), Parallel, SerialA full GC: Pause Full (System.gc())
ZGCA major collection: Major Collection (System.gc())
G1 with -XX:+ExplicitGCInvokesConcurrentA young pause, then a concurrent cycle
With -XX:+DisableExplicitGCNothing: the call is ignored

With -XX:+DisableExplicitGC, the JVM ignores calls to System.gc(), but it still runs a GC when it needs one.

“finalize() is the place to free resources”

No. finalize() is deprecated since Java 9, and deprecated for removal since Java 18 (JEP 421). The GC can call it very late, with no guaranteed delay. It runs on any thread, in any order. And if the finalizer thread cannot keep up, the objects that wait for it can fill the heap, up to an OutOfMemoryError.

Use instead:

  • a close() method, with AutoCloseable and try-with-resources: the close is guaranteed, even when an exception is thrown;
  • Cleaner (Java 9), to run a cleanup action when the object can no longer be reached.

To check that an application no longer depends on finalizers, you can start it with --finalization=disabled (since Java 18).

“More heap is always better”

Often true, but not always. Oracle’s guide says to give the JVM as much memory as possible, unless you have problems with pauses. The default size is often too small.

But there are limits:

  • the maximum heap must stay below the memory of the machine, or the system starts paging;
  • the JVM also uses memory outside the heap (Metaspace, thread stacks…): in a container, all of it must fit in the limit;
  • with G1, a bigger heap usually makes the marking phase longer.

The options to set the heap size are in the post about JVM options.

“Tuning the GC is the first thing to do”

No. Oracle’s guide says to start the application first and let the JVM choose the GC. For G1, its general advice is to keep the default settings, with at most a different pause time goal and a maximum heap size (-Xmx).

Before you change an option, look at what happens: the GC logs are a good start (-Xlog:gc*). And the guide reminds us: a change in the application can work better than tuning the JVM.

“The GC prevents memory leaks”

No. The GC frees the objects that nothing references anymore. But for Oracle, a memory leak in Java is exactly an application that keeps references to objects or classes without meaning to: a cache that only grows, a list that is never cleared… The GC cannot free them. The heap fills up, or the Metaspace for classes, up to an OutOfMemoryError.

There are also native memory leaks, outside the heap. For the ones inside the JVM itself, the Native Memory Tracking (NMT) tool helps to find them.

What has changed

What you still readWhat is true todaySince
Free resources in finalize()finalize() is deprecated for removal; use try-with-resources or CleanerJava 9 (deprecated), Java 18 (for removal)
A class leak fills the PermGenPermGen is gone: the Metaspace fills upJava 8
Tune the CMS optionsCMS is gone; G1 is the default GC, and its default settings are the adviceJava 9 (G1 default), Java 14 (CMS removed)

Sources: the javadoc of System.gc() and Object.finalize(), JEP 421 (finalization), the documentation of the java command, Oracle’s garbage collection guide and the troubleshooting guide about memory leaks (JDK 25).

Summary

System.gc() is an expensive request, finalize() is something to forget, a bigger heap helps as long as it fits in memory, you measure before you tune, and the GC does not protect you from references you forgot.

Further reading