5

这是我的代码

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() 会永远被阻塞。为什么我的可终结对象的幻像引用不能放入引用队列?被GC收集了吗?

4

1 回答 1

1

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

如果您将以这种风格修改您的程序:

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

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

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

  1. Java:PhantomReference、ReferenceQueue 和 finalize
  2. 使用非平凡终结器发现对象
  3. 终结者的秘密生活:第 2 页,共 2 页

UPD:另一种方式:你必须记住,非平凡的finalize()方法是由特殊的 Finalizer-Thread 调用的,它们也必须被收集。因此,对于完整的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) 检查标志是否为真,然后gc()再次调用:

    System.gc();
    while (!flag) Thread.sleep(10);
    System.gc();
于 2013-07-25T07:39:19.817 回答