JavaGarbage Collection

Garbage Collection

As covered on the Memory Management page, every object you create with new lives on the heap. In C or C++, you would eventually have to call free() (or delete) on that memory yourself, and forgetting to do so is one of the most common sources of bugs in those languages. Java removes that responsibility entirely: the JVM runs a background process called the garbage collector (GC) that automatically finds objects your program can no longer reach and reclaims their memory, with no explicit free() call anywhere in your code.

The general idea: mark and sweep

There are many garbage collection algorithms, but most share the same basic shape, often called mark-and-sweep:

  • Mark — starting from a set of "GC roots" (local variables on active stacks, static fields, and a few other anchor points), the collector walks every reference it can follow and marks each object it reaches as still alive.

  • Sweep — anything left unmarked after that walk is, by definition, unreachable from any running part of the program, so its memory can be reclaimed.

The key insight is that reachability, not reference counting or anything you explicitly declare, is what keeps an object alive. Once the last reference to an object disappears — a local variable goes out of scope, a field is overwritten, an entry is removed from a collection — the object becomes eligible for collection the next time the GC runs.

You cannot reliably force garbage collection
`System.gc()` looks like a command to run garbage collection right now, but it is only a **hint** to the JVM — the JVM is free to ignore it entirely. Relying on `System.gc()` to free memory at a specific moment is a well-known code smell: it doesn't guarantee anything happens, it can pause your whole application if it does run a full collection, and it usually signals that a design problem (holding references longer than needed) should be fixed instead.
Different collectors for different workloads

Modern JVMs ship with several garbage collector implementations, each tuned for different priorities — some favor overall throughput, others favor minimizing pause times for latency-sensitive applications. You don't need to master JVM tuning to write everyday Java code, but it is worth knowing the names exist:

  • G1 (Garbage-First) — the default collector on modern JVMs, balancing throughput and reasonably low pause times for most general-purpose applications.

  • ZGC — designed for very large heaps with extremely low pause times, aimed at latency-critical applications.

  • Older collectors like Parallel GC and CMS still exist in some JVM versions, optimized more purely for throughput or for pause times respectively.

This is deliberately high-level
Choosing and tuning a specific collector (heap sizing, pause-time goals, generational settings) is a deep topic on its own, mostly relevant once an application has real, measured GC-related performance problems. For day-to-day Java development, the default collector is almost always the right choice, and the important takeaway is simpler: write code that lets objects become unreachable as soon as you're done with them, and let the JVM handle the rest.
  • The JVM automatically reclaims heap memory for objects that are no longer reachable — no explicit free() call needed.

  • Mark-and-sweep is the general idea: mark everything reachable from GC roots, then reclaim everything left unmarked.

  • System.gc() is only a suggestion to the JVM, not a guaranteed command — relying on it is a code smell.

  • Different collectors (G1, ZGC, and others) exist and are tuned for different throughput vs. pause-time trade-offs.