【问题标题】:Why can I not get the PhantomReference from the ReferenceQueue for a finalizable object?为什么我不能从 ReferenceQueue 中获取可终结对象的 PhantomReference?
【发布时间】:2013-07-24 22:19:56
【问题描述】:

这是我的代码

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收集了吗?

【问题讨论】:

    标签: java reference finalize


    【解决方案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

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

    【讨论】:

    • 非常感谢。
      我知道FinalizerReferenceQueue和FinalizerDeamon,2次GC可以完全收集到一个finalizable对象。但我不明白为什么 100 次 GC 会触发它被收集(即使 20 次 GC 也无法触发入队)。
      另一方面,如果我将 PhantomReference 替换为 WeakReference,它可以非常快速地删除引用。
    • @DevboardFan 这是因为为了调用非平凡的finalize()方法,JVM构造了特殊的Finalizer-Thread,它也必须被收集和完成。
    • @DevboardFan BTW,您可以在Main() 类中创建public static volatile boolean flag;,在finalize() 方法中设置此标志,然后在Main() 中等待,直到设置此标志(在第一个@987654342 之后@)。之后您可以再拨打System.gc() 一次。这样就够了。
    • 我明白了。您的意思是 finalize() 方法将在 1 次 GC 后被调用,但对象仍然没有真正回收(从堆中)。我很奇怪为什么它需要这么多 GC 来触发真正的回收过程(而不是调用 finalize())。再次感谢您。
    猜你喜欢
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多