【问题标题】:WeakReference, reachabilityFence, and Java Memory ModelWeakReference、reachabilityFence 和 Java 内存模型
【发布时间】:2021-01-29 18:19:00
【问题描述】:

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

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

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

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

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

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

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

我怀疑 (c) 和 (d) 太模糊,因此不可能像我的第一个问题那样回答基本问题。

【问题讨论】:

  • 在“下一步,(d)reachabilityFence”之后我没有得到这部分。第一句话就像“它说一个电话……”,然后是一个问题,例如“我可以将它解释为说电话……”,使用与前一句相同的短语。当您假设一个句子的字面意思是它所说的内容时,这根本不是一种解释。但是,您继续“从技术上讲,它不是这么说的”。在那里,我迷路了。当它说它时,即使是字面意思,为什么不说它“技术上”?
  • 首先,对于我无法理解的写作感到抱歉。我的大脑有点过于数学化、怀疑和不灵活。但让我解释一下我的意思。文档说[它保持强大的可达性(但直到何时才说)]并且[使对象不可回收(
  • 它没有说“and”,它说“保持强可达性......因此引用的对象不可回收”。后者是前者的结果,因此即使您假设在这句话中“至少直到”仅指后者,由于逻辑依赖性,它也必须适用于前者。关于 JMM 中“至少直到”的含义,请注意语句“此方法仅适用于回收可能具有可见效果的情况,这对于具有终结器的对象是可能的......” 排序是关于栅栏前的动作和终结器或清洁器中的可见效果。

标签: java memory-model java-memory-model


【解决方案1】:

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

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

所以调用Reference.reachabilityFence(obj) 会建立一个strong referenceobjobj 被包裹在 WeakReference 中并不重要(这样“...可能会导致对象变得无法访问...”),它将保持强可访问性。

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

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

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

【讨论】:

  • 感谢您对 GC 工作原理的友好回答和全面解释。但这不是我关心的重点。我主要关心执行的重新排序(和线程间交互)——我认为这是 JLS (a) 和 (b2) 的要点。
  • 但我猜你对可达性围栏的看法可能是对的。那里的示例代码似乎暗示围栏至少会影响程序顺序中围栏之前的操作。如果是这样,我假设所有的动作都发生在栅栏受到影响之前,因为如果不是这样,java 将非常不直观。 (它不是从前者逻辑上推导出来的,就像你所做的那样。)我真的想区分规范保证了什么,可以从规范中猜到什么,以及常见的 jvm impl 的行为方式。这是第二个吧?
猜你喜欢
  • 1970-01-01
  • 2019-04-06
  • 1970-01-01
  • 2013-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-03
相关资源
最近更新 更多