Object Lifecycle — Junior¶
What? The object lifecycle is the sequence of phases a Java object passes through from the moment you write
newuntil the JVM reclaims its memory: allocation → field defaulting → constructor execution → reachable use → unreachable → finalization (rarely) → reclamation. How? You drive the early phases with thenewoperator and constructors. The JVM's garbage collector handles the late phases — you do not free objects manually.
1. The seven phases at a glance¶
new Foo() use the object GC sweeps it
│ │ │
▼ ▼ ▼
[1 allocate] [2 default] [3 construct] [4 reachable] [5 unreachable] [6 (finalize)] [7 reclaim]
| Phase | Who triggers it | What happens |
|---|---|---|
| 1 Allocate | new operator | Heap memory carved out for the object header + fields |
| 2 Default-init | JVM | All fields set to 0 / null / false |
| 3 Construct | constructor body | Field assignments and side effects from your <init> code |
| 4 Reachable | program holds a reference | Object is alive; methods can be called on it |
| 5 Unreachable | last reference is dropped | GC marks the object as garbage at next collection |
| 6 Finalize | (legacy) finalize() | Deprecated in Java 9+, removed for new types — avoid |
| 7 Reclaim | GC | Memory returned to the heap free-list / region |
Phase 6 is essentially dead in modern Java. Use
Cleaner(Java 9+) ortry-with-resourcesfor cleanup. We'll cover that insenior.md.
2. Allocation: what new actually does¶
The JVM performs (roughly) four steps:
- Resolve the class
Dog— load it from the classpath if not already loaded, link it, run<clinit>(static initializers) once. - Allocate memory on the heap big enough for the
Dogobject's header + fields. - Default-initialize every field to its type's zero value.
- Run the constructor (
<init>), which assigns final values and runs your code.
The reference returned by new lives on the stack (inside rex). The object itself lives on the heap.
stack heap
┌──────┐ ┌───────────────────┐
│ rex ─┼────────▶│ header (12-16 B) │
└──────┘ │ name = "Rex" │
│ age = 0 │
└───────────────────┘
3. Default values vs constructor values¶
Before the constructor body runs, every field gets its type's default value. Only then does the constructor overwrite some of them.
class Box {
int width; // default: 0
String label; // default: null
boolean sealed; // default: false
Box(int w) {
this.width = w; // overrides default
// label and sealed keep their defaults
}
}
Box b = new Box(10);
System.out.println(b.width); // 10
System.out.println(b.label); // null
System.out.println(b.sealed); // false
Why this matters: you can read a field inside a constructor before assigning it, and you'll see the default — not random garbage. Java is memory-safe by design.
| Type | Default |
|---|---|
int, long, short, byte | 0 |
float, double | 0.0 |
char | ' ' |
boolean | false |
| any reference type | null |
4. Constructors are the entry point¶
A constructor is a special method with no return type and the same name as the class. It runs exactly once per object — at birth.
public class Point {
private final double x;
private final double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
}
If you don't write any constructor, the compiler synthesizes a no-arg constructor for you. The moment you write any constructor, that gift disappears — you must declare a no-arg constructor explicitly if you still want one.
class A { } // synthetic no-arg constructor exists
class B { B(int x) { } } // no-arg gone — `new B()` won't compile
5. Reachability: the heart of garbage collection¶
An object is reachable if you can follow references from a GC root — a static field, a local variable on a live thread's stack, a JNI reference — and eventually arrive at it.
void scope() {
Dog rex = new Dog("Rex"); // reachable: local var rex points to it
rex.bark();
} // method returns; rex goes out of scope
// → Dog object is now unreachable, eligible for GC
The garbage collector cannot see "no one will use this object again." It only sees: is there a reference path from a root? If yes, alive. If no, garbage.
This means you can keep an object alive accidentally just by holding a reference you forgot about — that's a memory leak.
6. Going out of scope vs being collected¶
A common beginner confusion: "the variable went out of scope, so the object is freed." Not exactly.
Dog rex = new Dog("Rex");
// rex still in scope here
rex = null; // the *reference* is gone; object is now unreachable
// → eligible for GC, but GC may not run for ages
"Eligible for GC" ≠ "freed right now." The JVM decides when to actually run the collector. You should not assume any timing — write code that doesn't depend on when objects are reclaimed.
7. The constructor chain¶
When you call new SubClass(), the constructor chain runs from the topmost ancestor down:
class Animal {
Animal() { System.out.println("Animal ctor"); }
}
class Dog extends Animal {
Dog() { System.out.println("Dog ctor"); }
}
class Puppy extends Dog {
Puppy() { System.out.println("Puppy ctor"); }
}
new Puppy();
// Animal ctor
// Dog ctor
// Puppy ctor
Internally, the first statement of every constructor is implicitly super() unless you write super(...) or this(...) explicitly. This is what guarantees the parent is fully constructed before the child runs.
8. Static vs instance initialization¶
Two completely different lifecycles:
| Static (per class) | Instance (per object) |
|---|---|
| Runs once, at class-load time | Runs every new |
static fields | regular fields |
static { ... } blocks | { ... } blocks (rare) |
<clinit> in bytecode | <init> in bytecode |
class Counter {
static int total; // initialized once at class load
int id; // initialized each new
static { total = 0; } // static initializer
Counter() { id = ++total; } // constructor
}
We dive deeper into the order in middle.md.
9. Common newcomer pitfalls¶
Pitfall 1: leaking this from the constructor
Other threads could see a half-built Bad. Don't escape this from a constructor. Move registration to a factory method.
Pitfall 2: assuming finalize() runs
Use try-with-resources for closeable resources. Don't write finalize().
Pitfall 3: confusing null with "deleted"
Dog rex = new Dog("Rex");
rex = null;
// the variable is now null, but the OBJECT itself isn't "deleted" — it's just unreachable
// the JVM will collect it eventually
Pitfall 4: thinking constructors can return values
Constructors don't return anything (not even void). They initialize the object that new already allocated.
10. Quick mental checklist¶
When you write new Foo(args):
- Is
Fooloaded? If not, JVM loads it now (and runs<clinit>for the first time). - Allocate a
Foo-shaped chunk on the heap. - Default-init every field.
- Run
<init>(your constructor body, prefixed bysuper(...)orthis(...)). - Return the reference.
When you stop using an object:
- Drop all references (let them go out of scope, or assign
nullif needed). - Trust the GC. Do not call
System.gc()in production.
11. What to read next¶
| Question | Read |
|---|---|
| In what order do fields, blocks, and ctors run? | middle.md |
| How does GC actually work? G1, ZGC, generations? | senior.md |
What happens at the bytecode level (<init>/<clinit>)? | professional.md |
| What does the JLS say about instance creation? | specification.md |
Memorize this: An object's life is allocate → default → construct → use → unreachable → reclaim. You control the first three. The GC owns the last two. Anything between your new and the last reference dropping is use.