gpt4 book ai didi

Java PhantomReference 与 finalize()

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:37:44 25 4
gpt4 key购买 nike

我一直在阅读这篇关于 PhantomReference https://www.baeldung.com/java-phantom-reference 的文章以及在那里找到的简化示例代码:

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 方法后向引用队列添加一个幻象引用。这意味着该实例仍在内存中。”

所以我想进行测试并重写 finalize 方法,例如:

Object object = new Object() {
@Override
protected void finalize() throws Throwable {
System.out.println("finalize()");
}
};

但随后输出不同,幻象引用不再添加到队列中:

finalize()
isEnqueued() after GC: false

有人能解释一下为什么在此更改后输出不同以及如何更改此代码以便将幻像引用添加到队列中吗?

我一直在 JDK 8 和 11 上对此进行测试,两个平台上的结果相同。

最佳答案

语句“垃圾收集器在执行其引用对象的终结方法后,将幻像引用添加到引用队列。”充其量有点草率。

你应该引用the specification :

If the garbage collector determines at a certain point in time that the referent of a phantom reference is phantom reachable, then at that time or at some later time it will enqueue the reference.

鉴于“幻影可达”的链接定义指出:

An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.

因此,如果对象仅被幻象引用引用,那么“在执行其引用对象的 finalize 方法之后”对象是幻象可达的,因此将在该对象但不是立即之后入队。由于一个对象在其 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 循环之后异步发生,因此即使在垃圾收集器完成并检测到特殊的可达性状态时,在引用入队之前可能还需要额外的时间。


请注意“It implies that the instance is still in the memory”这句话。也没有做对,但在这种情况下,它基于甚至在 Java 核心开发人员方面的误解。

创建 API 时,在规范中添加了一句话,您甚至可以在 Java 8 版本中找到:

Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.

这可能会导致对象仍然必须在内存中的天真假设,但是 The Java® Language Specification状态:

Optimizing transformations of a program can be designed that reduce the number of objects that are reachable to be less than those which would naively be considered reachable.

简单地说,如果程序的行为没有改变,对象的内存可能会提前回收。这尤其适用于应用程序根本无法使用该对象的场景,例如幻影引用。如果对象不再在内存中,程序的行为将不会改变,因此您不能假设它确实在内存中。

这引出了一个问题,为什么要将不清除幻象引用的规则添加到规范中。正如在 this answer 中讨论的那样,那个问题被提出来,根本无法回答。因此,此规则已在 Java 9 中删除,幻象引用在入队时被清除,如弱引用和软引用。这是不假设对象仍在内存中的更有力的理由,因为现在即使是非优化环境也可以在此时回收对象的内存。

关于Java PhantomReference 与 finalize(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53822132/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com