Object lifecycle and GC in case of Composition



Below is an example of Composition:

public class A3 {
    B b = new B();
}

What I learned is that as soon as object of A3 is GC’ed (or becomes eligible for GC), object of B will also be GC’ed (or becomes eligible for GC).

Now, let’s consider below situation, so as per my understanding after d.m1();, a3 object on heap will become eligible for GC, but I don’t think that b object becomes eligible for GC.

My two understandings are contracting with each other, could someone please help me identify which one is wrong.

public class Testing {

    public static void main(String[] args) {
        D d = new D();
        d.m1();
        d.m2();
        // do some more things...
    }
}

    public class A3 {
        B b = new B();
        
        public B getB() {
            return b;
        }
    }
    
    public class B{
        public void m1() {
            System.out.println("B.m1");
        }
    }
    
    public class D{
        B bd2;
        
        public void m1() {
            A3 a3 = new A3();
            bd2 = a3.getB();
        }
        
        public void m2() {
            bd2.m1();
        }
    }

UPDATE: I do understand difference between “eligible for GC” and “actually getting GC’ed”, so for sake of convenience, let’s say object will immediately be GC’ed once it becomes eligible.

Answer

What @Shadab and @Koenigsberg has said is right, here is some explanation with a demo – basically what I did is I have overridden the finalize() method so as to show you how objects are getting GC’ed.

  • Key thing to note here is that any object created inside a method and not passed anywhere else from the method will be eligible for GC as soon as the method’s stack frame is taken out from the stack. That is why you will see “A3 is garbage collected” as soon as d.m1(); is over.
  • And for the same reason, you see “D is garbage collected” once test1(); is over. If you comment out test2(); then “D is garbage collected” will not be printed because once stack frame of test1 method was removed, there was no (forced) GC , and as long as stack frame of test1 is present on the stack, object d anyways cannot be removed.
  • GC of object ‘b’ is little tricky, if you don’t sleep thread for a few milli-seconds then it doesn’t get GC’ed, sadly I don’t know the exact explanation for this.

Another important point to note is you can check yourself on which objects can be GC’ed by starting from the root object, in this example object of class Testing will be your root object, so at each step you can try to see whether you can reach a particular object by starting from your root object or not, any object which is not reachable from your root tree becomes eligible for GC.

public class Testing {

    public static void main(String[] args) {
        test1();
        test2();
    }

    private static void test1() {
        Testing t = new Testing();
        D d = t.new D();
        d.m1();
        System.gc();
        d.m2();
        System.gc();
        // do some more things...
    }

    private static void test2() {
        System.gc();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.gc();
    }
}

public class A3 {
    B b = new B();

    public B getB() {
        return b;
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("A3 is garbage collected");
    }
}

public class B {
    public void m1() {
        System.out.println("B.m1");
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("B is garbage collected");
    }
}

public class D {
    B bd2;

    public void m1() {
        A3 a3 = new A3();
        bd2 = a3.getB();
    }

    public void m2() {
        bd2.m1();
    }

    @Override
    protected void finalize() throws Throwable {
        System.out.println("D is garbage collected");
    }
}


Source: stackoverflow