Please consider following two classes:
a.) Student
package datatypes;
public class Student {
private String name ;
public Student(String name) {
this.name = name;
}
class Address{
String city;
String state;
Address(String city , String state){
this.city = city;
this.state = state;
}
String getAddress(){
return city + state;
}
}
String getName(){
return name;
}
}
b.) StudentDemo
package datatypes;
import datatypes.Student.Address;
public class StudentDemo {
public static void main(String[] args) {
Student obj = new Student("Yati");
Address adr = obj.new Address("YNR" , "HARYANA");
System.out.println(obj.getName());
System.out.println(adr.getAddress());
obj=null;
//System.out.println(obj.getName());
System.out.println(adr.getAddress()); //Line 16
}
}
According to oracle java docs
For creating an instance of the inner class first we have to create an instance of the enclosing class and later we can create the instances of the inner class.
Like other members (instance variables and instance methods) inner class is also a member of the instance of the outer class. In line no 16 of StudentDemo.java I am still able to print the value of the address even if the object which was responsible for the creation of Address object DOES NOT exist in the memory.
My question is: why does the Address object remains in the memory and doesn't get destroyed automatically once the obj is set to null?
Output:
Yati
YNRHARYANA
YNRHARYANA
Setting a reference to null does not mean that the garbage collector immediately collects the corresponding object from memory!
And beyond that: the GC can't even collect your object; because adr
still has a reference to the "inner object"; and that one has a reference to its outer owner!
So, even when you would add a System.gc()
call after doing obj = null
... that object can not be collected. (and just for the record: calling gc()
is just a "hint" to the JVM to run the garbage collector; there are no guarantees that anything happens)
Long story short: of course things have to work this way. Your code holds a reference to an inner object; and while that reference is there, that object must exist. And while that inner object must exist; so must its outer parent. In other words: the whole system relies on the fact that the life time of inner/outer objects are tightly coupled!
And given your last comment - how is the runtime supposed to know that this inner object could live with its outer parent being null?! You might call a method on inner that actually uses outer.this; and then what, NullPointerException?!
So the real answer might be to understand that you asking a purely theoretical questions. In the "real" world you would simply not do that. In the real world, you would not hand out references to inner objects; not caring about their outer parent. Similar to Domain Driven Design - there an aggregation of objects is also only accepted via the root object of the aggregation (see here for example). That is of course not the same as objects within the JVM; but as said: an example that you simply do things differently for conceptual reasons.