长寿命Java WeakReferences

 2023-01-19    439  

问题描述

我目前正在尝试诊断应用程序中的记忆泄漏缓慢.到目前为止,我所拥有的事实如下.

  • 我从应用程序的4天运行中有一个堆转储.
  • 此堆转储包含〜800个弱rereference对象,该对象指向对象(所有类型的类型,我将其称为此问题的目的)保留40MB的内存.
  • Eclipse内存分析工具表明,这些弱含量所指的每个foo对象均未引用任何其他对象.我的期望是,这应该使这些foo对象弱到达,因此应在下一个GC中收集它们.
  • 这些Foo对象中的每个对象都有一个时间戳,表明它们是在4天运行过程中分配的.在此期间,我也有日志,确认垃圾收集正在发生.
  • 我的应用程序正在创建大量的foo对象,其中只有很小的一小部分在堆转储中处于这种状态.这向我表明根本原因是某种种族条件.
  • 我的应用程序使用JNI致电到本地库. JNI代码在一天开始初始化期间4次调用NewGlobalRef,以获取其使用的Java类的引用.

,尽管仅通过弱论引用(根据Eclipse Memory Analyzer工具),可能会导致这些FOO类不被收集吗?

长寿命Java WeakReferences

?

edit1:

@mindas
我正在使用的弱Reference等同于以下示例代码.

public class FooWeakRef extends WeakReference<Foo>
{
  public long longA;
  public long longB;
  public String stringA;

  public FooWeakRef(Foo xiObject, ReferenceQueue<Foo> xiQueue)
  {
    super(xiObject, xiQueue);
  }
}

foo没有最终制度,只要弱者未清除,任何终结者就不会考虑.当对象弱到达时,它是无法最终确定的. 请参阅此页面a>.

@kasten在对象最终确定之前,要清除弱论.我的堆转储表明这没有发生.

@jarnbjo我参考弱者javadoc:

“假设垃圾收集器在某个时间点确定一个物体是弱到达的.当时,它将在原子上清除对该对象的所有弱参考,以及所有弱参考任何其他可忽视的对象物体可以通过一系列强和软引用链.”

这向我表明,GC应该检测到我的foo对象”弱到达”和”当时”清除弱参考的事实.

编辑2

@J Flemm-我知道40MB听起来不那么多,但我担心4天内40MB在100天内意味着4000MB.我读过的所有文档都表明,弱接触的物体不应在几天内闲逛.因此,我对有关对象如何在不出现在堆中的参考的情况下强烈引用的任何其他解释感兴趣.

我将尝试分配一些大物体时,当存在这些悬空的foo对象,并查看JVM是否收集它们.但是,此测试将需要几天的设置和完成.

编辑3

@jarnbjo-我知道我不能保证何时JDK会注意到一个对象可忽略不可到达.但是,我希望在沉重负载下进行4天的申请将为GC提供足够的机会注意到我的对象很弱. 4天后,我非常怀疑剩下的弱参考物体已经以某种方式泄漏了.

编辑4

@j flemm-那真的很有趣!只是为了澄清,您是说GC在您的应用程序上发生并且没有清除软/虚弱的裁判吗?您能给我更多有关您正在使用的JVM + GC配置的详细信息吗?我的应用程序正在使用堆的80%的内存栏来触发GC.我假设任何旧gen的GC都会清除较弱的裁判.您是否建议一旦记忆使用量高于较高的门槛,GC只会收集弱参考?这个较高的极限是否可配置?

编辑5

@j flemm-您对在Softrefs之前清除弱者的评论与javadoc一致,该javadoc指出:
Softref:”假设垃圾收集器可以在某个时间点确定一个物体可轻轻地触及.当时 May 选择原子上清除所有对该对象的软引用,以及所有软引用其他可透视的对象可以通过一系列强引用链到达该对象.与此同时,或者在以后的某个时候,它将涉及以参考队列注册的那些新清除的软引用.”

.”

fealRef:”假设垃圾收集器在某个时间点确定对象可忽略.当时它将在原子上清除了对该对象的所有弱参考和所有弱参考该物体可以通过强烈和软引用链到达该对象的任何其他弱访问的物体.与此同时,它将声明所有以前的弱访问的物体以最终确定.与此同时.与此同时或在以后的某个时间将涉及以参考队列注册的那些新清除的弱参考.”

为了清楚起见,您是说垃圾收集器在您的应用程序具有超过50%的免费内存时运行,在这种情况下,垃圾收集器无法清楚地清楚弱点吗?当您的应用程序具有> 50%的免费内存时,为什么GC完全运行?我认为您的应用程序可能只是生成很少的垃圾,当收集器运行时,它正在清除弱Rrefs,而不是Softrefs.

编辑6

@j flemm-对您应用程序行为的另一种可能的解释是,正在收集年轻的gen,但是您的弱和软裁判都在旧的一代中,并且只有在收集旧的gen时才能清除.对于我的应用程序,我有统计数据表明正在收集旧的gen,这应该意味着弱者被清除.

编辑7

我正在就这个问题开始赏金.我正在寻找有关GC发生时如何无法清除弱者的任何合理解释.如果答案是这是不可能的,那么我理想地我希望指向openjdk的适当位,这些openjdk表明,一旦确定一个物体确定为薄弱,并且每次运行GC时都可以解决弱的可及性.

推荐答案

我终于可以检查热点JVM源代码并找到以下代码.

在ReferenceProcessor.cpp中:

void ReferenceProcessor::process_discovered_references(
  BoolObjectClosure*           is_alive,
  OopClosure*                  keep_alive,
  VoidClosure*                 complete_gc,
  AbstractRefProcTaskExecutor* task_executor) {
  NOT_PRODUCT(verify_ok_to_handle_reflists());

  assert(!enqueuing_is_done(), "If here enqueuing should not be complete");
  // Stop treating discovered references specially.
  disable_discovery();

  bool trace_time = PrintGCDetails && PrintReferenceGC;
  // Soft references
  {
    TraceTime tt("SoftReference", trace_time, false, gclog_or_tty);
    process_discovered_reflist(_discoveredSoftRefs, _current_soft_ref_policy, true,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

  update_soft_ref_master_clock();

  // Weak references
  {
    TraceTime tt("WeakReference", trace_time, false, gclog_or_tty);
    process_discovered_reflist(_discoveredWeakRefs, NULL, true,
                               is_alive, keep_alive, complete_gc, task_executor);
  }

函数process_discovered_reflist具有以下签名:

void
ReferenceProcessor::process_discovered_reflist(
  DiscoveredList               refs_lists[],
  ReferencePolicy*             policy,
  bool                         clear_referent,
  BoolObjectClosure*           is_alive,
  OopClosure*                  keep_alive,
  VoidClosure*                 complete_gc,
  AbstractRefProcTaskExecutor* task_executor)

这表明ReferenceProcessor :: process_discovered_references.

搜索process_discovered_reference的热点代码显示CMS Collector(我正在使用的是)从以下呼叫堆栈调用此方法.

CMSCollector::refProcessingWork
CMSCollector::checkpointRootsFinalWork
CMSCollector::checkpointRootsFinal

此调用堆栈看起来每次运行CMS集合时都会调用它.

假设这是正确的,长期活着的弱引用对象的唯一解释要么是一个微妙的JVM错误,要么是GC未运行.

其他推荐答案

您可能需要检查是否泄漏了ClassLoader问题.有关此主题的更多信息,您可以在 this 博客文章

其他推荐答案

您需要澄清Foo和WeakReference之间的链接是什么.情况

class Wrapper<T> extends WeakReference<T> {
  private final T referent;
  public Wrapper(T referent) {
    super(t);
    this.referent = referent;
  }
}

有很大不同

class Wrapper<T> extends WeakReferece<T> {
  public Wrapper(T referent) {
    super(t);
  }
}

或其内线版本,WeakReference<Foo> wr = new WeakReference<Foo>(foo).

所以我认为您的案例不像我在我的第一个代码片段中所描述的那样.

正如您所说的,您正在与JNI合作,您可能想检查是否有任何不安全的最终化器.每个终结器都应具有finally块调用super.finalize(),并且很容易滑动.

您可能需要告诉我们更多有关您对象的性质以提供更好的想法的信息.

以上所述是小编给大家介绍的长寿命Java WeakReferences,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对77isp云服务器技术网的支持!

原文链接:https://77isp.com/post/25755.html

=========================================

https://77isp.com/ 为 “云服务器技术网” 唯一官方服务平台,请勿相信其他任何渠道。