Object Lifecycle and Reachability
Every Java object goes through a lifecycle: it is created with new, used while the program can still reach it, becomes unreachable when no live reference leads to it any more, and is finally collected by the garbage collector, which reuses its memory. You never free memory yourself. Whether an object can be collected depends on one idea: reachability. In this lesson, you will learn the stages of an object's life and the common ways objects become unreachable.
The Stages
| Stage | What happens |
|---|---|
| Created | Memory is allocated on the heap, fields get default values, then the constructor runs |
| Reachable | Some chain of references from a GC root (a live local variable, a static field, an active thread) leads to it |
| Unreachable | No chain from any GC root reaches it any more |
| Collected | At a time the GC chooses, its memory is reclaimed |
An object is not collected the moment it becomes unreachable. It is only eligible; the garbage collector reclaims it when it next runs over that part of the heap.
How Objects Become Unreachable
| Way | Example |
|---|---|
The reference is set to null | s = null; |
| The reference is pointed elsewhere | s = new Student("Srihitha", 78); |
| The method that held it returns | A local variable goes out of scope |
| It's removed from a collection | list.remove(s); |
| Only unreachable objects refer to it | Two objects pointing at each other, but nothing else at them |
Example: Watching an Object Become Collectable
Java doesn't let you observe collection directly, but a WeakReference lets you check whether an object is still alive without keeping it alive (see "Strong, Weak, Soft and Phantom References").
import java.lang.ref.WeakReference;
public class Lifecycle {
record Student(String name) { }
public static void main(String[] args) throws InterruptedException {
Student s = new Student("Navika");
WeakReference<Student> watcher = new WeakReference<>(s);
System.gc();
System.out.println("While referenced: " + watcher.get());
s = null;
System.gc();
Thread.sleep(100);
System.out.println("After s = null and a GC: " + watcher.get());
Student a = new Student("Srihitha");
Student b = new Student("Advitha");
Object[] cycle = {a, b};
WeakReference<Object[]> cycleWatcher = new WeakReference<>(cycle);
a = null;
b = null;
cycle = null;
System.gc();
Thread.sleep(100);
System.out.println("Unreachable group collected? "
+ (cycleWatcher.get() == null));
}
}While referenced: Student[name=Navika]
After s = null and a GC: null
Unreachable group collected? trueExplanation: While s refers to the student, a garbage collection leaves it alone. After s = null, nothing strong refers to it, so the next collection reclaims it and the weak reference returns null. Objects that refer only to each other, with no path from a GC root, are also collected: Java's garbage collector traces from roots, so cycles are not a problem.
About System.gc()
Common Mistakes
Setting every variable to null "to help the GC". Local variables become unreachable when the method ends; this is rarely needed.
Expecting objects to be collected right away. Collection happens later, when the GC decides.
Forgetting that collections keep objects alive. An object stored in a static list is reachable forever (see "Memory Leaks in Java").
Practice
- Easy: Create an object, point the only reference at a second object, and use a
WeakReferenceto show the first object can be collected. - Harder: Store an object in a static
List, set your local reference tonull, and show that it is not collected. Then remove it from the list and show that it is.
Key Takeaways
Was this tutorial helpful?