为什么在重写 finalize 方法时引用不放入引用队列
why the reference don't put into reference queue when finalize method overrided
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
但是如果我删除了classA中的finalize方法,结果是:
java.lang.ref.PhantomReference@5b2c9e5d
所以,结果显示,当我覆盖finalize方法时,弱对象没有被放入引用队列,是因为aObject复活了吗?但是我在finalize方法中没有做任何事情
非常有趣的观察。这是正在发生的事情:
当class有non-trivial(在OP情况下非空)finalize
方法JVM将创建一个java.lang.ref.Finalizer
(这是一个子class Reference
) 对象并将其指向我们的对象,在本例中为 A 对象。这反过来将阻止 PhantomReference
将其入队,因为 A 已被 Finalizer
.
引用
这是我在使用 Java 1.8 的调试器中观察到的,并且还详细描述了 here。
来自@Turing85 的观察是预期的,因为当我们删除 finalize
方法中的所有语句时,它变成 trivial 并且表现得像任何没有 finalize
方法的 class 和不会被 Finalizer
.
引用
更新:
有人问 Finalizer
是否会完全清除它对 A 的引用。 JVM 会在随后的 GC 运行中清除它,这反过来最终允许 PhantomReference
将 A 排入其引用队列。
例如 运行 下面带有非平凡 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() );
}
打印:
finalize
java.lang.ref.PhantomReference@15db9742
对于非平凡的 finalize
,Java 在 finalize
运行之前知道对象无法访问*,但不知道对象 仍然 在 finalize
.
之后无法访问
它必须等待对象在另一个 GC 周期中再次被视为不可访问,然后才能将幻像引用排入队列。
*在java.lang.ref docs的术语中不是完全不可到达,但既不是强可达、软可达也不是弱可达
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
但是如果我删除了classA中的finalize方法,结果是:
java.lang.ref.PhantomReference@5b2c9e5d
所以,结果显示,当我覆盖finalize方法时,弱对象没有被放入引用队列,是因为aObject复活了吗?但是我在finalize方法中没有做任何事情
非常有趣的观察。这是正在发生的事情:
当class有non-trivial(在OP情况下非空)finalize
方法JVM将创建一个java.lang.ref.Finalizer
(这是一个子class Reference
) 对象并将其指向我们的对象,在本例中为 A 对象。这反过来将阻止 PhantomReference
将其入队,因为 A 已被 Finalizer
.
这是我在使用 Java 1.8 的调试器中观察到的,并且还详细描述了 here。
来自@Turing85 的观察是预期的,因为当我们删除 finalize
方法中的所有语句时,它变成 trivial 并且表现得像任何没有 finalize
方法的 class 和不会被 Finalizer
.
更新:
有人问 Finalizer
是否会完全清除它对 A 的引用。 JVM 会在随后的 GC 运行中清除它,这反过来最终允许 PhantomReference
将 A 排入其引用队列。
例如 运行 下面带有非平凡 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() );
}
打印:
finalize
java.lang.ref.PhantomReference@15db9742
对于非平凡的 finalize
,Java 在 finalize
运行之前知道对象无法访问*,但不知道对象 仍然 在 finalize
.
它必须等待对象在另一个 GC 周期中再次被视为不可访问,然后才能将幻像引用排入队列。
*在java.lang.ref docs的术语中不是完全不可到达,但既不是强可达、软可达也不是弱可达