Java needs garbage collection because keeping track of when every heap object can safely be freed is a fragile job for application code. The garbage collector reclaims ordinary objects that are no longer reachable from live computation, including disconnected groups of objects that refer to one another. But an object that remains reachable—such as an obsolete entry in a long-lived cache—cannot be collected just because the program no longer needs it.
Why freeing memory by hand is fragile
In a language that requires manual memory management, code that allocates an object must also decide when it is safe to release it. That decision has two common failure modes:
- Free too early: another part of the program still uses the object. A later access can become a dangling reference, causing incorrect behavior or a crash.
- Free too late—or forget: the program keeps memory it no longer needs, reducing the memory available for useful work.
Java automates reclamation for ordinary heap objects, so application code does not pair each allocation with an ordinary explicit free operation. The JVM determines which objects are eligible for reclamation. This removes much of the routine object-lifetime bookkeeping from application code, though it does not eliminate every memory-management problem.
How reachability determines what can be collected
For garbage-collection purposes, the central question is whether an object is reachable from the program’s live references, often described as roots. HotSpot’s implementation guide defines an object as garbage when it can no longer be reached from references of live objects. The collector traces outward from roots; objects it cannot reach are eligible for reclamation.
In this simplified graph, Program represents a live root. It can reach A, and A can reach B. C and D refer to one another, but neither is connected to the root:
Program (root) → A → B C ⇄ D
A and B are reachable, so they remain live for garbage-collection purposes. C and D form a disconnected cycle. Because tracing from the root cannot reach either one, both are eligible for collection even though they still refer to each other. This explanation describes reachability, not a claim that every Java collector uses one particular tracing algorithm. HotSpot’s [garbage-collector implementation guide](https://docs.oracle.com/en/java/javase/22/gctuning/garbage-collector-implementation.html) and [OpenJ9’s GC overview](https://eclipse-openj9.github.io/openj9-docs/gc_overview/) provide implementation context; the [Java reference API](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/ref/package-summary.html) describes the reference-related API.
Rank #2
Why counting references can fail on cycles
A reference-counting scheme tracks how many references point to each object and can reclaim an object when its count reaches zero. In the disconnected cycle above, C still has an incoming reference from D, and D still has one from C. Their counts need not fall to zero, even though no live root can reach either object. A count of incoming references alone does not establish whether the program can reach an object.
Root-based reachability handles this case by asking whether there is a path from live computation to an object, rather than whether the object has any references at all. This is an algorithmic contrast: the cited Java and JVM references explain roots and reachability, but do not assert that every Java collector works in one simple way or make a formal claim about all reference-counting systems.
Why Java programs can still leak memory
Garbage collection cannot infer whether reachable data is still useful to the program. If a long-lived collection or global cache still refers to an object, that object remains reachable and cannot be reclaimed—even if the application has no further use for it. If such unintended retained references accumulate, the program can suffer a memory leak.
This is the key distinction: unreachable garbage is eligible for collection; reachable but unwanted data is not. A leak in a garbage-collected program is often a retention problem, not a failure to manually free each object. Oracle’s [memory-leak troubleshooting guide](https://docs.oracle.com/en/java/javase/26/troubleshoot/troubleshooting-memory-leaks.html) discusses unintended retention and leak diagnosis.
Rank #4
Does calling System.gc() force collection?
No. System.gc() and Runtime.gc() request garbage collection, but they do not guarantee that collection will happen immediately or that a particular amount of memory will be recovered. The Java SE 26 [Runtime API](https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/lang/Runtime.html) says: “The Java Virtual Machine performs this recycling process automatically as needed, in a separate thread, even if the gc method is not invoked explicitly.” The API describes an explicit request as a best effort, not a command with a timing or recovery guarantee.
What an OutOfMemoryError does—and does not—tell you
An OutOfMemoryError means the JVM could not satisfy a memory allocation; by itself, it does not prove that the program has a leak. Unintended retained references are one possible cause, but heap capacity or configuration can also be insufficient for the workload. Oracle’s [troubleshooting guide](https://docs.oracle.com/en/java/javase/26/troubleshoot/troubleshooting-memory-leaks.html) covers both leak symptoms and memory-related diagnosis. Distinguishing a leak from insufficient heap requires examining the program’s memory behavior rather than treating the error alone as a diagnosis.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Quick Recap
Best Value
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

