WeakReference、reachabilityFence 和 Java 内存模型

问题描述

假设 ref一个 WeakReference 对象,指向(或在某个时刻指向)一个对象 obj。如果调用 ref.get() 发生在(或至少在程序顺序之前?)Reference.reachabilityFence(obj) 的执行,是否有任何机会ref.get() 的返回者可能是 null 吗? (另外,通过ref.get()通过获取refnull的观察部分,我想再问一次这个问题/em> 通过队列轮询。)

我问这个问题是因为我不太确定 Reference#reachabilityFenceJava Memory Model 方面是如何工作的,尤其是当它与 WeakReference(和其他类似引用)交互时。以下列表是我所知道的与该主题相关的规范存在的所有地方:

问题在于,与 (a) 和 (b) 中使用的术语相比,(c) 和 (d) 中使用的术语含糊不清。

如果开头写的问题的答案是肯定的,那么我怎样才能使所有 ref.get() 调用发生在某个代码点之前?如果不是,我们如何根据 (a)(b)(c)(d) 或其他任何地方编写的规范证明这一点?

虽然第一个问题的答案对我来说可能已经足够了,但我也希望(c)和(d)中的规范变得清晰,所以让我在下面提出更多问题。

首先,(c)WeakReference 表示在某个时间点 gc 确定对象是弱可达的,同时清除弱引用,同时声明这些对象作为最终确定。根据 (b2),可达性检查和可终结性声明发生在可达性决策点。所以“某个时间点”一定是可达性决策点之一,对吗如果是这样,其他问题就会出现。 (b2) 还说可达性决定点不是代码或程序顺序中的实际点(与写入和读取不同);它们是仅通过前/后关系与动作相关的虚拟点,与程序排序无关。所以......弱引用在决策点被清除,这不在代码的任何地方。 究竟是什么意思?在什么情况下WeakReference#get()可以为空?不能为空?我们需要的是决策点和 WeakReference#get()间的前后关系吗?或者决策点和其他一些与WeakReference#get()有一定内存模型相关关系的动作之间的前/后关系?

接下来,(d)reachabilityFence。它说这个方法调用保持了对象的强可达性和 从而使其在调用之后无法回收。我可以将其解释为调用保持对象的强可达性直到调用 之后吗? 从技术上讲,它不是这么说的。但是如果没有规定什么时候保持强可达性,那么保持强可达性的部分就没有用了。 (另一半谈论未定义的概念“不可回收性”从一开始就没有用。)所以强可达性一直保持到调用之后——但是“之后”这个词究竟是什么意思程序顺序?以前发生过吗?佣金发生的顺序?此外,我认为对象在(b2)中描述的某个决策点变得无法访问。是什么决定了某个对reachabilityFence 的调用是否影响某个决策点?围栏和决策点之间的前后关系?显然不是,因为fence的调用既不是写也不是读也不是同步动作,所以某个fence调用是在某个决策点之前还是之后没有区别。

我怀疑 (c) 和 (d) 太含糊,因此无法像我的第一个问题那样回答基本问题。

解决方法

回答你的第一个问题:不。这正是文档所说的:

确保给定引用所引用的对象保持强可达,无论程序之前的任何操作可能导致对象变得不可达;因此,引用的对象不能被垃圾收集回收至少在调用此方法之后

因此调用 Reference.reachabilityFence(obj) 建立了一个 strong referenceobj。将 obj 包裹在 WeakReference 中的内容无关紧要(因此“...否则可能导致对象无法访问...”),它将保持强可达。

我不确定在某个时间点您正在寻找什么,但这可能会有所帮助。 GC 循环可能只扫描(并因此发现弱引用)堆的部分区域。它只能扫描一些区域(在像 G1 这样的区域化收集器的情况下),因此只需跳过您的 weak reference 可能所在的区域。这意味着弱引用的发现和清除是不确定的。一些 GC 算法甚至安排了这些特殊引用的收集:也就是说,即使他们现在发现它,他们也可能在下一个 X 周期跳过清除它。您可以有效地延迟有意地清除此类引用 (ShenandoahRefProcFrequency)。它发生,但不知道什么时候发生,也不知道在哪个 GC 周期中发生。

这可能就是可达性决策点。这个决定是在 GC 遇到这种特殊引用时做出的。

我也不确定 after 一词的混淆来自哪里。由于 Reference.reachabilityFence(obj); 的调用通常应该只发生在一个方法中,对我来说这就是程序顺序,根据 JLS is 也是 happens-before 关系。