java 代码优化的危险是将弱引用用于局部变量中记住的强引用?[已结束]

Danger of java code optimization for the usage of the weak references to strong references remembered in local variables? [closed]

提问人:Artur Linhart 提问时间:5/2/2023 更新时间:5/2/2023 访问量:61

问:


想改进这个问题吗?通过编辑这篇文章添加详细信息并澄清问题。

7个月前关闭。

我遇到过这样的问题,有时我对某个对象的弱引用就消失了,即使我在局部变量中记住了强引用。但在我看来,这可能与JIT优化有关。 今天,在 Java 11 中,在我看来,我可以找到这种行为的可重复确认。因此,在局部变量中记住了对弱引用的某些内容的引用的情况下,它可能不会阻止该对象进行垃圾回收。我可以使用以下构造重现该问题:

SomeObjectStructure locId = generateIdAndRememberInternallyWeakReferenceToIdObject();
// This did not work:
// Reference.reachabilityFence(locId);
// The only working solution for my singleton was to assign 
// the Id to the ThreadLocal field value of the singleton:
// currentThreadId.set(locId)
try {
  // do some stuff here, but do not work with variable locId
  // directly, only try to ask for the weak reference sometimes
  somethingAskingWeakReferenceToId();
  // In the above method sometimes, the weak reference shows null, so
  // even if one would think, the local variable locId 
  // is preventing the object from garbage collection, it does not.
  // There were situations where at this time the id has
  // already gone. In other cases it was still there.
  // This has then serious consequences if you rely 
  // on the presence of the Id in the weak reference 
  // container in the code here:
  doSomethingOnTheBasisOfContentOfRememberedWeakReference();
} finally {
  // Following statement has been intended to release the strong reference 
  // locId to allow the GC of the the weakly internally remembered Id
  // after the finally block ends, but 
  // sometimes this can be ignored by the compiler 
  // or optimizer, the variable 
  // could be released much earlier than after this place in the code:
  locId = null;
  // Instead of the above statement I used later this one:
  // currentThreadId.set(null);
  // to release the locId remembered in the ThreadLocal variable 
  // of class containing the code.
}

我找到了 Oracle 优化的页面,类似的东西也可能从中得出结论,请参阅此处。我说得对吗?或者对这种行为有其他解释吗?如果我在一些 ThreadLocal 变量中另外记住了 id(我的对象是单例),那么问题也消失了。同样在这个问题中,讨论了类似的问题,并提出了使用的解决方案

Reference.reachabilityFence(locId);

在分配变量之后,我也进行了测试,但在我的情况下,这没有帮助,变量内容在到达最终块之前仍然被垃圾收集......为什么会这样?

但是带有栅栏的解决方案也有缺点,如果我理解正确的话,应该记住变量内容,而不是在变量的可见性范围结束之前,而是在方法完成之前。但它仍然没有奏效......

有没有其他方法可以防止编译器/优化器的这种行为?

java 垃圾回收 jit 弱引用

评论

0赞 Louis Wasserman 5/2/2023
局部变量是否再次使用过?关于可达性的规则非常具体。
2赞 John Bollinger 5/2/2023
当你说“我对某个对象的弱引用刚刚消失”时,你的意思是你发现弱引用对象已被清除,以至于它的方法返回?get()null

答:

3赞 John Bollinger 5/2/2023 #1

我遇到过这样的问题,有时我对某个对象的弱引用就消失了,即使我在局部变量中记住了强引用。

弱引用的本质是它不会阻止被引用的对象被 GC'd。我知道,在对同一对象具有强引用的局部变量的范围内发生这种情况是令人惊讶的,但这实际上是可能的。由于您描述了使用 Java 11 执行测试,以下是 JLS 11 的相关文本:

可访问对象是可以在任何潜在环境中访问的任何对象 从任何实时线程继续计算。

可以从某个可终结对象访问终结器可访问对象 对象通过一些引用链,但不是来自任何活动线程。

无法通过任何一种方式到达无法到达的对象

这必须仔细阅读。特别要注意的是,可访问对象的定义不是(仅)基于所持有的引用,而是基于该对象是否实际可以访问,这是一个更强的条件。(通过对象的访问被明确排除在外,尽管不是通过该文本。如果没有任何线程可以执行的代码路径来访问相关对象,则无法访问该对象。Reference

该文本是专门为允许在您演示的情况下收集对象而设计的,其中有一个或多个强引用,但是,根据程序的实际代码,可以确定该对象实际上不会被访问(除非通过对象)。Reference

或者对这种行为有其他解释吗?

不需要特别的解释。您描述的是允许的行为。它可能更有可能由已 JIT 的代码执行,但这将是一个功能,而不是一个错误。

同样在这个问题中讨论了类似的问题,并且 建议使用的解决方案

Reference.reachabilityFence(locId);

在分配变量之后,我也测试了它,但是在我的 如果这没有帮助,变量内容仍然是垃圾 在到达最终区块之前收集...为什么会这样?

您误解了 .它不会阻止指定的对象在将来变得不可访问,而是防止过去变得不可访问。因此,在变量赋值之后立即使用它毫无意义。另一方面,在示例的块中使用它应该可以防止在块的执行终止之前收集对象(或清除引用)。Reference.reachabilityFence()finallytry

有没有其他方法可以防止编译器/优化器的这种行为?

见上文。但是,如果这给您带来了麻烦,那么您可能滥用了弱引用。因此,另一种解决方案是改用强引用。

评论

2赞 Holger 5/2/2023
值得注意的是,即使访问对象也不一定会阻止其收集,因为优化器可以将代码转换为无需访问即可工作的形式。规范:“可以设计优化程序的转换,将可访问的对象数量减少到少于天真地认为可访问的对象数量 另请参阅这个已知的现实生活问题
0赞 Artur Linhart 5/3/2023
非常感谢,尤其是对RechabilityFence的澄清,这是我以前不知道的......
0赞 Artur Linhart 5/3/2023
@Holger - 是的,这也是我从 Oracle 的例子中得出的结论......感谢您分享澄清可访问性的链接。