How does garbage collection work in Java, and what makes an object garbage?
Java uses reachability, not reference counting. An object is garbage when it is no longer reachable from the roots of the object graph. The roots are the references held by local variables on the thread stacks, by static fields, by the runtime's own bookkeeping, and by JNI handles. The collector traces outward from the roots, and anything it does not reach is garbage.
That is a fundamentally different algorithm from reference counting, and it is why Java does not have the classic problems of that approach. There is no need to run a finaliser or a weak-reference callback when an object's count drops to zero, so there is no delay in reclaiming an object that is part of a cycle. And there is no per-object counter to maintain, so allocating an object costs a pointer write rather than an increment.
The cost of tracing is that it is proportional to the number of live objects, not to the number of dead ones. Collecting a lot of short-lived garbage is cheap; collecting a huge live data set is expensive. This is the single fact that explains most of the tuning advice: the young generation exists precisely so that the vast majority of objects die before they are old enough to be expensive to trace.
One more thing the collector cannot do. It only frees memory it knows is unreachable, so it cannot tell you that an object is no longer useful. A cache that is full of entries nobody reads is perfectly reachable, perfectly live, and will never be collected. That is a memory leak of the most common kind in Java, and it is why caches need an eviction policy rather than a garbage collector.
Here is the same thing as runnable code:
public class Main {
public static void main(String[] args) {
// Once nothing references an object, it is eligible for collection.
Object reference = new Object();
System.out.println("alive : " + (reference != null));
reference = null;
System.out.println("dropped : " + (reference == null));
// A weak reference tells the collector it may reclaim the object.
java.lang.ref.WeakReference<String> weak =
new java.lang.ref.WeakReference<>(new String("reclaimable"));
System.out.println("weak before : " + weak.get());
System.gc(); // a hint, not a command
System.out.println("gc requested");
}
}