The Object Lifecycle in Java
From new to unreachable: how objects are created, how long they live, and what actually happens when nothing references them any more.
-
Java Basics
- Introduction to Java
- Setting Up Java and Writing Your First Program
- Variables, Data Types and Literals in Java
- Type Casting and Type Conversion in Java
- Operators and Expressions in Java
- Input and Output in Java
- Comments, Keywords and Naming Conventions in Java
- Control Flow in Java: if, else and switch
- Loops in Java: for, while and do-while
- Methods
- Arrays and Strings
-
OOP
- Classes and Objects in Java
- Constructors in Java
- The this Keyword in Java
- The static Keyword in Java
- Encapsulation in Java
- Access Modifiers in Java
- Inheritance in Java
- Method Overriding and super in Java
- Polymorphism in Java
- Abstraction, Abstract Classes and Interfaces in Java
- Composition, Aggregation and Association in Java
- The Object Lifecycle in Java
- Core Java
- Exception Handling
-
Collections
- The Java Collections Framework
- List in Java: ArrayList, LinkedList, Vector and Stack
- Set in Java: HashSet, LinkedHashSet and TreeSet
- Map in Java: HashMap, LinkedHashMap and TreeMap
- How HashMap Works Internally in Java
- Queue and Deque in Java: ArrayDeque and PriorityQueue
- Iterators in Java
- Comparable and Comparator in Java
- Collections Utilities and Choosing the Right Collection
- Generics
- Java 8+
- Stream API
- Date and Time
- File and I/O
-
Multithreading
- Threads in Java: Processes, Runnable and Thread
- Thread Lifecycle in Java
- Synchronization in Java: synchronized and volatile
- Locks and Atomic Classes in Java
- Race Conditions and Deadlocks in Java
- The Executor Framework and Thread Pools in Java
- Future and CompletableFuture in Java
- Concurrent Collections in Java
- The Java Memory Model
- JVM and Memory
- Advanced Java
- Networking
- JDBC
- Testing
The stages
- Creation - memory is allocated and the object is initialised.
- Use - the object is reachable through at least one reference.
- Unreachable - no live reference remains.
- Collection - the garbage collector reclaims the memory, at a time it chooses.
Creation, step by step
Note note = new Note("Java basics");- The class is loaded and initialised, if it has not been already.
- Memory is allocated on the heap.
- All fields are set to their default values.
- The superclass constructor runs, up the chain to
Object. - Field initialisers and instance initialiser blocks run, in source order.
- The constructor body runs.
- The reference is assigned to the variable.
class Base {
Base() { System.out.println("2 Base constructor"); }
}
class Derived extends Base {
private String value = init(); // 3
{ System.out.println("4 instance block"); }
static { System.out.println("1 static block, once per class"); }
Derived() {
super(); // implicit if omitted
System.out.println("5 Derived constructor");
}
private String init() {
System.out.println("3 field initialiser");
return "ready";
}
}Reachability
Note a = new Note("first");
Note b = a; // one object, two references
a = null; // still reachable through b
b = null; // now unreachable, eligible for collectionAn object becomes eligible for collection when no chain of references from a GC root reaches it. Roots include local variables of live threads, static fields and active thread objects.
GC roots
|
+--> [ Service ] --> [ Cache ] --> [ Entry ] reachable, kept
|
[ OldNote ] --> [ Detail ] unreachable island, collectedTwo objects referring to each other are still collected if nothing outside reaches them. Java uses reachability tracing, not reference counting, so cycles are not a problem.
Scope and lifetime are different things
List<Note> all = new ArrayList<>();
void add() {
Note note = new Note("kept"); // the variable ends with the method
all.add(note); // the object survives, the list references it
}A local variable disappears at the end of its block. The object it referred to lives as long as anything still points at it.
What garbage collection guarantees
- It reclaims memory for unreachable objects.
- It does not promise to run at any particular moment.
- It does not release files, sockets or database connections.
System.gc(); // a request, not a command; the JVM may ignore itReleasing resources properly
try (BufferedReader reader = Files.newBufferedReader(path)) {
return reader.readLine();
} // close() is called automatically, in reverse order, even on an exceptionAnything holding an operating system resource should implement AutoCloseable and be used with try with resources. This is deterministic, unlike collection.
finalize is gone, and Cleaner is rarely needed
The old finalize() method was deprecated and then removed, because it ran unpredictably, could resurrect objects and delayed collection. For the rare case of a safety net over a native resource, java.lang.ref.Cleaner exists. It is not a substitute for closing resources explicitly.
Generations, in one paragraph
Most objects die young, so the heap is usually split into a young generation and an old one. New objects are allocated in the young generation and collected there cheaply; survivors are promoted. This is why creating many short lived objects is far less costly than intuition suggests.
Common mistakes
- Believing
System.gc()collects immediately. - Relying on collection to close a file or a connection.
- Holding objects in a long lived static collection and never removing them, which is the most common Java memory leak.
- Registering a listener and never removing it.
- Assuming
nullassignment frees memory at once. It only removes one reference.
Best practices
- Keep references only as long as the object is genuinely needed.
- Use try with resources for anything closeable.
- Remove entries from long lived caches, or use a cache with an eviction policy.
- Prefer immutable objects, which are simpler to reason about and easy for the collector.
- Do not write code that depends on when collection happens.
Practice
- Predict the printed order of the
Derivedexample, then run it. - At which line does the object created in
new Note("first")become eligible for collection? - Explain why two objects that reference each other are still collected.
- Rewrite a manual
close()in afinallyblock as try with resources and say what improved. - Describe a realistic scenario where a static
Mapcauses a memory leak, and how to fix it.
Conclusion
An object lives as long as something reachable refers to it, and no longer. Let the collector handle memory, close resources yourself, and watch long lived collections, because that is where objects overstay.