Life and Death
of an Object
COMP2396 Object-Oriented Programming and Java
Dr. Kenneth Wong
The Stack and the Heap
In Java, we care about 2 areas of memory, namely the stack
and the heap
The stack
Memory set aside as a scratch space for a thread of execution
Used to store method invocations and local variables
Last-In-First-Out (LIFO)
The Heap
Memory set aside for dynamic allocation
Also known as the garbage-collectible heap
Where all objects live
1
Methods are Stacked
When a method is called, a new stack frame is created and
pushed onto the top of the stack
A stack frame holds the state of the method, including which
line of code is executing and the values of all local variables
The method at the top of the stack is always the currently
running method for that stack
A method stays on the stack until it hits its closing curly
brace top of stack
stack
Example frames
bar() s
If foo() calls bar(), bar() is foo() x i b
local
stacked on the top of foo() variables
bottom of stack
2
A Stack Scenario
Example
public void doStuff() { 1 Code from another class calls
boolean b = true; doStuff(), and doStuff() goes
go(4); into a stack frame at the top of
} the stack. The boolean
variable named ‘b’ goes on the
public void go(int x) { doStuff() stack frame
int z = x + 24;
crazy();
// imagine more code here
}
public void crazy() { doStuff() b
char c = 'a';
}
3
A Stack Scenario
Example
public void doStuff() { 2 doStuff() calls go(), go() is
boolean b = true; pushed on top of the stack.
go(4); Variables ‘x’ and ‘z’ are on the
} go() stack frame
public void go(int x) {
int z = x + 24;
crazy();
// imagine more code here
}
go() x z
public void crazy() { doStuff() b
char c = 'a';
}
4
A Stack Scenario
Example
public void doStuff() { 3 go() calls crazy(), crazy() is
boolean b = true; now on the top of the stack,
go(4); with variable ‘c’ on the frame
}
public void go(int x) {
int z = x + 24;
crazy();
// imagine more code here crazy() c
}
go() x z
public void crazy() { doStuff() b
char c = 'a';
}
5
A Stack Scenario
Example
public void doStuff() { 4 crazy() completes, and its
boolean b = true; stack frame is popped off the
go(4); stack. Execution goes back to
} the go() method, and picks up
at the line following the call to
public void go(int x) { crazy()
int z = x + 24;
crazy();
// imagine more code here
}
go() x z
public void crazy() { doStuff() b
char c = 'a';
}
6
Local Variables for Objects
Remember, a non-primitive variable holds a reference to an
object, but not the object itself
For a local variable holding a reference to an object, only the
variable goes on the stack, the object still lives on the heap
Example
public class StackRef { barf() declares a reference variable
public void foof() { ‘d’ and creates a new Duck object.
barf(); Since ‘d’ is declared inside the
method, it is a local variable and goes
}
on the stack Duck
object
public void barf() {
Duck d = new Duck(24);
barf() d
}
The Duck object
} foof() lives on the heap
7
Instance Variables
If local variables live on the stack,
where do instance variable live?
Instance variables live inside the object they belong to, and
therefore they also live on the heap
Example
public class CellPhone {
priavte int x; x ant
private Antenna ant; int Antenna
}
CellPhone
object
When a CellPhone object is created,
its instance variables ‘x’ and ‘ant’ live
inside the object on the heap
8
Instance Variables
If local variables live on the stack,
where do instance variable live?
Instance variables live inside the object they belong to, and
therefore they also live on the heap
Antenna
Example object
public class CellPhone {
All objects live on the
priavte int x; x ant
heap, regardless of
private Antenna ant = new Antenna(); int Antenna whether the reference
} is a local or instance
CellPhone variable
object
When a CellPhone object is created,
its instance variables ‘x’ and ‘ant’ live
inside the object on the heap
9
The Role of Superclass Constructors
Recall that every object holds not just its own declared
instance variables, but also everything from its superclasses
Conceptually, an object can be thought of as having layers
of itself representing each superclass and its own class
Note that private instance variables in
a superclass, though not inherited by
a subclass object, also forms part of
the superclass part of the object Object
A subclass object might inherit methods Animal
that depend on private instance variables Hippo
of the superclass!
10
The Role of Superclass Constructors
When an object is being created, all the constructors
(including those of the abstract classes) up the inheritance
tree must run to build out both the superclass parts and its
own class part of the object
When a constructor runs, it immediately calls its superclass
constructor, which in turn immediately calls its superclass
constructor…
This calling of superclass constructor will go all the way up
the hierarchy until the Object class constructor is reached
This process is known as constructor chaining
11
Constructor Chaining
Example
public abstract class Animal {
public Animal() { Object
System.out.println("Making an Animal");
}
}
public class Hippo extends Animal {
Animal
public Hippo() {
System.out.println("Making a Hippo");
}
}
public class TestHippo { Hippo
public static void main(String[] args) {
System.out.println("Starting...");
Hippo h = new Hippo();
}
} 12
Constructor Chaining
Example
public abstract class Animal { 1 The main() in the TestHippo
public Animal() { class says new Hippo() and the
System.out.println("Making an Animal"); Hippo() constructor goes into a
} stack frame at the top of the
} stack
public class Hippo extends Animal {
public Hippo() {
System.out.println("Making a Hippo");
}
}
public class TestHippo {
public static void main(String[] args) { Hippo()
System.out.println("Starting...");
Hippo h = new Hippo();
}
} 13
Constructor Chaining
Example
public abstract class Animal { 2 Hippo() invokes the superclass
public Animal() { constructor which pushes the
System.out.println("Making an Animal"); Animal() constructor onto the
} top of the stack
}
public class Hippo extends Animal {
public Hippo() {
System.out.println("Making a Hippo");
}
}
Animal()
public class TestHippo {
public static void main(String[] args) { Hippo()
System.out.println("Starting...");
Hippo h = new Hippo();
}
} 14
Constructor Chaining
Example
public abstract class Animal { 3 Animal() invokes the
public Animal() { superclass constructor which
System.out.println("Making an Animal"); pushes the Object()
} constructor onto the top of the
} stack (Object is the superclass
of Animal!)
public class Hippo extends Animal {
public Hippo() {
System.out.println("Making a Hippo");
}
} Object()
Animal()
public class TestHippo {
public static void main(String[] args) { Hippo()
System.out.println("Starting...");
Hippo h = new Hippo();
}
} 15
Constructor Chaining
Example
public abstract class Animal { 4 Object() completes, and its
public Animal() { stack frame is popped off the
System.out.println("Making an Animal"); stack. Execution goes back to
} the Animal() constructor, and
} picks up at the line following
Animal’s call to its superclass
public class Hippo extends Animal { constructor
public Hippo() {
System.out.println("Making a Hippo");
}
}
Animal()
public class TestHippo {
public static void main(String[] args) { Hippo()
System.out.println("Starting...");
Hippo h = new Hippo();
}
} 16
Constructor Chaining
Example Sample output
public abstract class Animal { Starting...
public Animal() { Making an Animal
System.out.println("Making an Animal"); Making a Hippo
}
}
public class Hippo extends Animal {
public Hippo() {
System.out.println("Making a Hippo");
}
}
public class TestHippo {
public static void main(String[] args) {
System.out.println("Starting...");
Hippo h = new Hippo();
}
} 17
Child Cannot Exist Before Parents
Remember, a subclass object might depend on things it
inherits from its superclass
Hence, the superclass parts of an object have to be fully
formed before the subclass part can be constructed
This implies the superclass constructor must finish before its
subclass constructor
The call to super() must therefore be the first statement in
each constructor!
The complier will put a call to super() as the first statement
in each of your overloaded constructors if you have not done
so!
18
Child Cannot Exist Before Parents
Example: Identify problems in the constructors
public Ball() { super(); }
No problem. The first
statement being an
explicit call to super()
public Ball(int i) { super(); size = i; }
public Ball() {}
No problem. The compiler
will put a call to super() as
the first statement
public Ball(int i) { size = i; }
public Ball(int i) { size = i; super(); } This won’t compile!
Cannot explicitly put a call to
super() below anything else
19
Invoking an Overloaded Constructor
It is possible to call a constructor from another overloaded constructor
in the same class using this() with appropriate arguments
The call to this() can only be used in a constructor, and must be the
first statement in a constructor
A constructor can have a call to either super() or this(), but never
both!
Example
import java.awt.Color;
class MiniCooper extends Car {
public MiniCooper() { this(Color.RED); }
public MiniCooper(Color color) {
super("Mini Cooper");
this.color = color;
// more initialization
}
}
20
How Long Does a Variable Live?
How long does a variable live depends on whether the
variable is a local variable or an instance variable
A local variable is alive as long as its stack frame is on
the stack. In other words, until the method completes
A local variable is in scope only within the method in
which it is declared
When its own method calls another, the variable is
alive, but not in scope until its method resumes
The variable can only be used when it is in scope
21
The Difference between Life & Scope
Example
public void doStuff() { 1 doStuff() goes into a stack
boolean b = true; frame at the top of the stack.
go(4); Variable ‘b’ is alive and in
} scope
public void go(int x) {
int z = x + 24;
crazy();
// imagine more code here
}
public void crazy() { doStuff() b
char c = 'a';
}
22
The Difference between Life & Scope
Example
public void doStuff() { 2 go() is pushed on top of the
boolean b = true; stack. Variables ‘x’ and ‘z’ are
go(4); alive and in scope, and ‘b’ is
} alive but not in scope.
public void go(int x) {
int z = x + 24;
crazy();
// imagine more code here
}
go() x z
public void crazy() { doStuff() b
char c = 'a';
}
23
The Difference between Life & Scope
Example
public void doStuff() { 3 crazy() is pushed on top of the
boolean b = true; stack, with ‘c’ now alive and in
go(4); scope. The other 3 variables
} are alive but out of scope
public void go(int x) {
int z = x + 24;
crazy();
// imagine more code here crazy() c
}
go() x z
public void crazy() { doStuff() b
char c = 'a';
}
24
The Difference between Life & Scope
Example
public void doStuff() { 4 crazy() completes and is
boolean b = true; popped off the stack, so ‘c’ is
go(4); out of scope and dead. When
} go() resumes where it left off,
‘x’ and ‘z’ are both alive and
public void go(int x) { back in scope. Variable ‘b’ is
int z = x + 24; still alive but out of scope
crazy();
// imagine more code here
}
go() x z
public void crazy() { doStuff() b
char c = 'a';
}
25
How Long Does a Variable Live?
What about instance variables?
An instance variable lives as long as the object does,
and is scoped to the life of the object
In other words, instance variables live and die with the
object they belong
What about reference variable?
The rules are the same for primitives and references
A reference variable can be used only when it is in
scope
26
How Long Does an Object Live?
An object is alive as long as there are live references to
it
If a reference variable goes out of scope but is still
alive, the object it refers to is still alive on the heap
If the stack frame holding a reference gets popped off
the stack and that is the only live reference to the
object, the object is now abandoned on the heap and
becomes eligible for garbage collection
More precisely, an object becomes eligible for garbage
collection when its last live reference disappears
27
How Long Does an Object Live?
3 ways to get rid of an object’s reference
The reference variable goes out of scope permanently
void go () { The reference ‘z’ dies at
Life z = new Life(); the end of the method
}
The reference variable is assigned another object
Life z = new Life(); The first object is abandoned when ‘z’ is
z = new Life(); ‘re-programmed’ to a new object
The reference variable is explicitly set to null
Life z = new Life();
The first object is abandoned
z = null; when ‘z’ is ‘de-programmed’
28
Example: Object Killer #1
Example: Reference goes out of scope permanently
public class StackRef { 1 Code from another class calls
public void foof { foof(), and foof() is pushed
barf(); on top of the stack, with no
} variables declared
public void barf() {
Duck d = new Duck();
}
}
foof()
29
Example: Object Killer #1
Example: Reference goes out of scope permanently
public class StackRef { 2 foof() calls barf(), and barf()
public void foof { is pushed on top of the stack.
barf(); barf() declares a reference
} variable ‘d’, creates a new
Duck object on the heap, and
public void barf() { assigns its reference to ‘d’. ‘d’
Duck d = new Duck(); is alive and in scope
}
}
Duck
object
barf() d
foof()
30
Example: Object Killer #1
Example: Reference goes out of scope permanently
public class StackRef { 3 barf() completes and is
public void foof { popped off the stack. Its stack
barf(); frame disintegrates, so ‘d’ is
} now dead and gone. The Duck
object is now abandoned and
public void barf() { becomes eligible for garbage
Duck d = new Duck(); collection. Execution returns to
} foof()
}
Duck
object
foof() eligible for garbage
collection
31
Example: Object Killer #2
Example: Assign the reference to another object
public class ReRef { 1 The new Duck object goes on
Duck d = new Duck(); the heap and is referenced by
the instance variable ‘d’. The
public void go() { Duck object will live as long as
d = new Duck(); the ReRef object that
} instantiated it is alive, unless…
}
Duck
object
Duck
ReRef object
32
Example: Object Killer #2
Example: Assign the reference to another object
public class ReRef { 2 Someone calls go() where ‘d’
Duck d = new Duck(); is assigned a new Duck
object, leaving the first Duck
public void go() { object abandoned which
d = new Duck(); becomes eligible for garbage
} collection
}
eligible for garbage
collection
Duck
object
Duck Duck
object
ReRef object
33
Example: Object Killer #3
Example: Explicitly set the reference to null
public class ReRef { 1 The new Duck object goes on
Duck d = new Duck(); the heap and is referenced by
the instance variable ‘d’. The
public void go() { Duck object will live as long as
d = null; the ReRef object that
} instantiated it is alive, unless…
}
Duck
object
Duck
ReRef object
34
Example: Object Killer #3
Example: Explicitly set the reference to null
public class ReRef { 2 Someone calls go() where ‘d’
Duck d = new Duck(); is set to null, leaving the Duck
object abandoned which
public void go() { becomes eligible for garbage
d = null; collection
}
}
eligible for garbage
collection
Duck
object
Duck
ReRef object
35