Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I not get the PhantomReference from the ReferenceQueue for a finalizable object?

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?

like image 277
Devboard Fan Avatar asked Nov 13 '22 01:11

Devboard Fan


1 Answers

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();
like image 110
Andremoniy Avatar answered Nov 14 '22 21:11

Andremoniy