为什么我不能从ReferenceQueue获取PhantomReference以获得可终结的对象?

时间:2013-07-25 06:51:08

标签: java reference finalize

这是我的代码

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();
        }
    }
}

这很奇怪,rq.remove()将永远被阻止。为什么我的finalizable对象的幻像引用不能放入引用队列?它是否被GC收集?

1 个答案:

答案 0 :(得分:1)

问题在于您的非平凡finalize()方法。在默认实现中(在类Object中),此方法实际上是空的。当其实现不为空时,则无法保证在调用finalize()后立即收集对象。

如果您将以这种方式修改您的程序:

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

即。你会多次调用GC一次 - 它可能导致rq对象被完全收集(在我的机器上测试)。

另外,我建议您按照以下链接进行探索:

  1. Java: PhantomReference, ReferenceQueue and finalize
  2. Discovering Objects with Non-trivial Finalizers
  3. The Secret Life Of The Finalizer: page 2 of 2
  4. UPD:另一种方式:您必须牢记,特殊的Finalizer-Thread会调用非平凡的finalize()方法,这些方法也必须收集。因此,对于完整的pr收集,您可以执行以下操作:

    a)在Main方法中创建标志:

    public static volatile boolean flag;
    

    b)在finalize()方法中设置标志:

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

    c)检查标志为true,然后再次呼叫gc()

        System.gc();
        while (!flag) Thread.sleep(10);
        System.gc();