public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object object = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);
object = null;
System.gc();
Thread.sleep(1_000);
System.out.println("isEnqueued() after GC: " + phantomReference.isEnqueued());
Reference reference = referenceQueue.poll();
if(reference != null) {
System.out.println("isEnqueued() after poll(): " + phantomReference.isEnqueued());
}
}
以下是输出:
isEnqueued() after GC: true
isEnqueued() after poll(): false
所以一切都按预期工作,对对象的强引用被设置为null,这是由GC检测到的,幻影引用被添加到队列中。
在这篇文章中,他们说:“垃圾收集器在执行其引用对象的finalize方法后,将一个幻影引用添加到引用队列中。这意味着实例仍然在内存中。”
Object object = new Object() {
@Override
protected void finalize() throws Throwable {
System.out.println("finalize()");
}
};
finalize()
isEnqueued() after GC: false
语句“垃圾回收器在执行其referent的finalize方法后向引用队列添加幻影引用。”充其量有点草率。
您应该参考规范:
如果垃圾回收器在某个时间点确定幻影引用的引用是幻影可达的,那么在那个时间或稍后的某个时间,它将使该引用排队。
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object object = new Object() {
@Override
protected void finalize() throws Throwable {
System.out.println("finalize()");
}
};
PhantomReference<Object> phantomReference = new PhantomReference<>(object, referenceQueue);
object = null;
System.gc();
Thread.sleep(1_000);
System.gc();
Thread.sleep(1_000);
System.out.println("isEnqueued() after GC: " + phantomReference.isEnqueued());
Reference reference = referenceQueue.poll();
if(reference != null) {
System.out.println("isEnqueued() after poll(): " + phantomReference.isEnqueued());
}
您很可能会看到所需的输出,但必须强调的是,不能保证垃圾回收器在调用system.gc()
时会真正运行,也不能保证它在运行时会在特定的时间内完成,或者在特定的周期内会找到所有无法访问的对象。此外,排队是在gc循环之后异步进行的,因此即使在垃圾回收器完成并检测到特殊的可达性状态时,在它被排队之前也可能会经过额外的时间。
请注意,“它暗示实例仍然在内存中。”这句话也不正确,但在本例中,这是基于Java核心开发人员的误解。
创建API时,在规范中添加了一句话,您甚至可以在Java8版本中找到这句话:
可以设计程序的优化转换,以减少可达对象的数量,使其少于那些可能被认为是可达的对象。
简单地说,如果程序的行为不改变,对象的内存可能会更早地被回收。这特别适用于应用程序根本不能使用对象的场景,比如幻影引用。如果对象不在内存中,程序的行为将不会改变,所以您不能假设它实际上在内存中。
这就引出了一个问题,为什么幻影引用不被清除的规则被添加到规范中。正如本答案中所讨论的,那个问题被提出来,根本无法回答。因此,该规则在Java9中被删除,幻影引用在像弱引用和软引用一样排队时被清除。这是一个更强有力的理由,不假设对象仍然在内存中,因为现在即使是非优化的环境也可以在此时回收对象的内存。