Question

Here is my code

public class FinalizableObject {

    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize() invoked for " + this);
        super.finalize();
    }
}

public class Main {
    private static void test() throws InterruptedException {
        ReferenceQueue<FinalizableObject> rq = new ReferenceQueue<FinalizableObject>();
        FinalizableObject obj = new FinalizableObject();
        PhantomReference<FinalizableObject> pr1 = new PhantomReference<FinalizableObject>(obj, rq);
        obj = null;
        System.gc();
        Reference<? extends Object> ref = rq.remove();
        System.out.print("remove " + ref + " from reference queue\n");
    }
    public static void main(String[] args) {
        try {
            test();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

It's very strange, rq.remove() will be blocked forever. Why my finalizable object's phantom reference can not be put into reference queue? Has it been GC collected?

Was it helpful?

Solution

The problem is with your non-trivial finalize() method. In default implementation (in class Object) this method is actually empty. When its implementation isn't empty, then it is not guaranteed that object will be instantly collected after invoking finalize().

If you will modify your program in such style:

    for (int i = 0; i  < 1000; i++)
        System.gc();

i.e. you will call GC more then one times - it could lead to the rq object be fully collected (test on my machine).

Also, I suggest you following links to explore:

  1. Java: PhantomReference, ReferenceQueue and finalize
  2. Discovering Objects with Non-trivial Finalizers
  3. The Secret Life Of The Finalizer: page 2 of 2

UPD: Another way: you have to hold in mind, that non-trivial finalize() methods is invoked by special Finalizer-Thread, which also have to be collected. So, for full pr collecting you can do such things:

a) create flag inside Main method:

public static volatile boolean flag;

b) set flag in finalize() method:

@Override
protected void finalize() throws Throwable {
    System.out.println("finalize() invoked for " + this);
    super.finalize();
    Main.flag = true;
}

c) check flag for true and then call gc() again:

    System.gc();
    while (!flag) Thread.sleep(10);
    System.gc();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top