Understanding Object Liveness Determination in Java: Reference Counting and Reachability Analysis
The article explains how Java determines whether an object is alive using two main garbage‑collection algorithms—Reference Counting and Reachability Analysis—detailing their mechanisms, limitations such as circular references, the role of GC Roots, finalization, and method‑area reclamation, with illustrative code examples.
This question is frequently asked in Java interviews; the answer follows the explanation from the book "Deep Understanding of the Java Virtual Machine" and covers how to judge whether an object is alive.
The two primary algorithms for determining object liveness are Reference Counting and Reachability Analysis.
Reference Counting adds a counter to each object, increments it when a new reference is created, and decrements it when a reference is lost; a counter of zero indicates the object is no longer reachable. Languages such as Objective‑C and Python use this method, but the Java VM does not because it cannot easily handle circular references.
Example of circular references in Java:
public class ReferenceCountingGC {
public Object instance = null;
public static void testGC() {
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
// Assume GC occurs here, can objA and objB be reclaimed?
System.gc();
}
}In this code, objA and objB reference each other via the instance field. After setting the local variables to null , the two objects are still mutually reachable, so their reference counts never drop to zero and the reference‑counting algorithm cannot reclaim them.
Reachability Analysis starts from a set of "GC Roots" and traverses the object graph; any object not reachable from these roots is considered dead.
Even objects deemed unreachable by reachability analysis are not immediately reclaimed. They first undergo a finalization check: if the object overrides finalize() and it has not yet been executed, the object is placed in an internal F‑Queue . A low‑priority finalizer thread later invokes finalize() . If the object does not need finalization or the method has already run, it proceeds to the second marking phase and is finally reclaimed.
Regarding the method area, the JVM specification allows implementations to omit garbage collection there because the cost‑benefit is low. When GC does occur in the method area, it mainly reclaims two kinds of waste: obsolete constants in the constant pool and unused classes. A class is considered unused when (1) all its instances have been reclaimed, (2) its defining ClassLoader has been reclaimed, and (3) its java.lang.Class object is no longer referenced.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.