Garbage collection reclaims heap memory by finding objects that are no longer reachable from live runtime roots and making their space available for future allocation.
Learning Question
How does the JVM know which objects can be freed?
A common misunderstanding is that garbage collection frees objects when the programmer is “done” with them. The JVM cannot read programmer intention. It reasons from runtime reachability.
The first mental model is:
An object is alive if it is reachable from GC roots. An unreachable object can be reclaimed.
Roots and Reachability
GC starts from roots: references the JVM treats as starting points for live object discovery.
Important roots can include:
- references in active thread stacks
- static fields of loaded classes
- JNI or native references
- JVM internal references
- references held by synchronization, class loading, or runtime services
From those roots, the collector follows references through objects and arrays. Every object reached through that graph is considered live for that collection. Objects not reached are reclaimable.
This is graph reachability, not source-code scope by itself.
Variables Do Not Get Deleted by GC
Garbage collection reclaims heap objects, not source variables.
A local variable slot may hold a reference. If that reference is live at a collection point, it can keep an object reachable. If no root path reaches the object anymore, the object can be collected.
The important question is not:
Did I stop using this variable in my head?
The important question is:
Is there still a runtime reference path from a root to this object?
Generational Collection
Many JVM garbage collectors use or have used generational ideas.
The generational hypothesis is that many objects die young, while some objects live much longer. A collector can use this observation to collect newer objects more frequently and older objects differently.
The exact heap layout and collector behavior differ across collectors and Java versions. Young generation, old generation, regions, evacuation, compaction, concurrent marking, and remembered sets are collector-specific details.
The durable developer-facing idea is:
Allocation creates pressure, and GC work discovers live objects and reclaims or reorganizes heap space according to the collector’s strategy.
Stop-the-World and Concurrent Work
Some GC work requires application threads to stop. This is often called a stop-the-world pause.
Other GC work can happen concurrently with application threads. Modern collectors often try to move more work out of long pauses, but they still need coordination points and may still pause application execution for specific phases.
This is why GC logs can show both pause events and concurrent phases.
A pause is not automatically a bug. The question is whether pause frequency, duration, and cause match the application’s latency and throughput requirements.
OutOfMemoryError
OutOfMemoryError does not always mean the same memory area failed.
Common examples include:
Java heap space: heap allocation could not be satisfiedMetaspace: class metadata memory could not be expandedDirect buffer memory: direct buffer allocation exceeded available or configured limitsUnable to create new native thread: the JVM or operating system could not create another thread
For heap OOM, the JVM usually cannot find enough reclaimable heap space to satisfy allocation. The cause might be a true memory leak, a legitimate working set larger than the heap, excessive allocation rate, unbounded caches, retained collections, or request patterns that create too much live data.
Memory Leaks in Java
In Java, a memory leak usually means objects remain reachable even though the application no longer needs them.
The GC is doing its job: it preserves reachable objects. The bug is that the application keeps references it should have removed.
Common causes include:
- unbounded maps or caches
- static collections
- listeners not unregistered
- thread-local values retained too long
- class loader leaks in redeployed environments
- queues that grow faster than they drain
Core Mental Model
Keep these boundaries separate:
- GC reclaims heap objects, not source variables.
- Reachability from roots determines liveness for GC.
- Objects can be unwanted by the developer but still reachable to the JVM.
- Different collectors use different strategies to find, move, compact, or reclaim objects.
- GC pauses come from runtime coordination, not from a simple idea of “deleting garbage.”
OutOfMemoryErrormust be mapped to the memory area that failed.
Final Summary
Garbage collection is runtime reachability analysis plus heap reclamation.
To reason about memory, ask which objects remain reachable, which roots keep them alive, and which memory area is actually under pressure.