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

Dev*_*Fan 5 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

And*_*niy 1

问题在于你的方法不简单finalize()。在默认实现中(在类中Object)这个方法实际上是空的。当其实现不为空时,不保证调用后会立即收集对象finalize()

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

    for (int i = 0; i  < 1000; i++)
        System.gc();
Run Code Online (Sandbox Code Playgroud)

也就是说,您将多次调用 GC - 这可能会导致rq对象被完全收集(在我的机器上测试)。

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

  1. Java:PhantomReference、ReferenceQueue 和 Finalize
  2. 使用重要的终结器发现对象
  3. 终结者的秘密生活:第 2 页(共 2 页)

UPD:另一种方式:您必须记住,非平凡的finalize()方法是由特殊的 Finalizer-Thread 调用的,也必须收集这些方法。因此,为了完整pr收集,您可以执行以下操作:

a) 在方法内创建标志Main

public static volatile boolean flag;
Run Code Online (Sandbox Code Playgroud)

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

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

c) 检查标志是否为 true,然后gc()再次调用:

    System.gc();
    while (!flag) Thread.sleep(10);
    System.gc();
Run Code Online (Sandbox Code Playgroud)