public class Test {
public static void main(String[] args) throws Exception {
A aObject = new A();
ReferenceQueue<A> queue = new ReferenceQueue<>();
PhantomReference<A> weak = new PhantomReference<>(aObject, queue);
aObject = null;
System.gc();
TimeUnit.SECONDS.sleep(1);
System.out.println(queue.poll());
}
}
class A{
@Override
protected void finalize() throws Throwable {
// TODO Auto-generated method stub
super.finalize();
System.out.println("finalize");
}
}
结果是:
finalize
null
但是如果我删除A类中的finalize方法,结果是:
java.lang.ref.PhantomReference@5b2c9e5d
所以,结果显示当我覆盖finalize方法时,弱对象没有被放入引用队列,那是因为aObject复活了吗?但我在finalize方法中没有做任何事情
答案 0 :(得分:4)
对于一个非常重要的finalize
,Java知道在finalize
运行之前对象是无法访问的*,但是在{{{{}}之后它还不知道对象仍然无法访问1}}。
必须等待该对象在另一个GC循环中再次被视为无法访问,然后才能将幻像引用排入队列。
*在java.lang.ref docs的术语中并不完全无法达到,但既不强烈,轻柔,也无法达到
答案 1 :(得分:3)
非常有趣的观察。以下是发生的事情:
当类具有non-trivial(在OP情况下为非空)finalize
方法时,JVM将创建java.lang.ref.Finalizer
(它是Reference
)对象的子类并指向它对我们的指称,在这种情况下是一个对象。这反过来会阻止PhantomReference
将其排入队列,因为Finalizer
已经引用了A. {/ p>
这是我在使用Java 1.8的调试器中的观察者,并且还详细描述了here。
@ Turing85的观察是预期的,因为当我们删除finalize
方法中的所有语句时,它变为trivial并且行为就像任何没有finalize
方法的类一样,并且不会被{{{ 1}}。
<强>更新强>
有人询问Finalizer
是否会明确其对A的引用。 JVM会在随后的GC运行中清除它,最终允许Finalizer
将A排入其引用队列。
例如,使用非平凡的PhantomReference
方法运行以下代码将从其引用队列中获取非空finalize
。
PhantomReference
打印:
public static void main(String[] args) throws Exception {
A aObject = new A();
ReferenceQueue<A> queue = new ReferenceQueue<>();
PhantomReference<A> pr = new PhantomReference<>(aObject, queue);
aObject = null;
System.gc();
TimeUnit.SECONDS.sleep(1);
System.gc();
TimeUnit.SECONDS.sleep(1);
System.out.println( queue.poll() );
}