3

我想了解垃圾收集器在什么情况下会通知您清除软引用。文档清楚地说明了当可能发生 OOM 时会发生这种情况,但是如果您完全丢弃引用怎么办,GC 会出现并收集 SoftReference 可能会注意到不存在其他强/软引用并清除内部引用值。

ReferenceQueue<Object> cleared = new ReferenceQueue<>();
Object X = new Object();
SoftReference<Object> Xs = new SoftReference<>(X, cleared);

Thread.sleep(10);
X = null;
Xs = null;

System.GC();
Thread.sleep(100);
Reference ref;
while ((ref = cleared.poll()) != null) {
   System.err.println(ref);
}

那么这个队列的规则记录在哪里呢?必须有比我要问的更多的极端案例,所以也许这个问题必须扩大一点。

4

2 回答 2

6

相关点可以在Notification部分的包文档中找到:

注册的引用对象与其队列之间的关系是片面的。也就是说,队列不会跟踪向其注册的引用。如果已注册的引用本身变得无法访问,那么它将永远不会入队。只要程序对其所指对象感兴趣,程序就有责任使用引用对象来确保对象保持可访问性。

所以答案很简单。由于您设置了引用SoftReferenceto的唯一变量null,因此它永远不会被排队。它是否会被收集或清除,没关系,因为你无法感知它。在实践中,典型的实现会像 Eugene 写的那样,垃圾收集器只遍历实时引用,并不关心剩余内存中存在什么样的垃圾。

于 2020-06-02T09:50:53.710 回答
4

在我看来,你有不止一个问题。第一个是引用将出现在引用队列中的时间。

文档(故意?)为解释留下了空间,取自此处

提供引用对象类,它们支持与垃圾收集器进行有限程度的交互。程序可以使用引用对象来维护对某个其他对象的引用,这样后一个对象仍然可以被收集器回收。程序也可以安排在收集器确定给定对象的可达性已更改后的某个时间得到通知。

所以,它发生. 在当前实现下它是一个异步进程,所以文档没有错。

您的第二个问题始于一个错误的前提:

... GC 会过来收集 SoftReference ...

并不真地。如果无法访问,GC 甚至不会知道此 SoftReference 的存在。GC 会找到活着的对象,而其他一切都是垃圾,听起来很奇怪。因此,GC 甚至不会知道存在 SoftReference,因为它从未访问过它。发生这种情况时,不会在ReferenceQueue. 为了发生这种情况,SoftReference需要遍历,但因为这从未发生过......

仅仅因为 aSoftReference不可达并不意味着所指对象也一样。所指对象是强可达的,但 是不可达的,这是完全有效的SoftReference

到你的最后一点(我暗示你会关心):当 SR 是可达的,但所指的对象已经死了。当 GC 发现这样的事情时,它应该清除所指对象(随后get将返回null)并将其发布到ReferenceQueue,对吗?嗯,差不多。这取决于实际的 GC。举个反例,取Shenandoah GC一个名为ShenandoahRefProcFrequency(默认为 5)的标志。它显示了应该多久清除一次弱引用,如果不是5,当这种情况发生时:引用对象被声明为“活动的”。在达到该限制之前,该指示物会被人为地保持活动状态。

于 2020-06-02T02:41:12.323 回答