当前位置: 首页 > 知识库问答 >
问题:

Java PhantomReference vs finalize()

池兴邦
2023-03-14
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

共有1个答案

吕自怡
2023-03-14

语句“垃圾回收器在执行其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中被删除,幻影引用在像弱引用和软引用一样排队时被清除。这是一个更强有力的理由,不假设对象仍然在内存中,因为现在即使是非优化的环境也可以在此时回收对象的内存。

 类似资料:

相关问答

相关文章

相关阅读