简体   繁体   English

VisualVM 堆转储“Summary”显示的根比“Objects->Preset: GC Roots”显示的多,这是什么意思?

[英]VisualVM heap dump "Summary" shows way more roots than "Objects->Preset: GC Roots" shows, what does it mean?

I suspect a slow memory leak caused by JNI code.我怀疑由 JNI 代码引起的缓慢 memory 泄漏。 I'm seeing the GC Roots # in the summary of consecutive heap dumps climbing.我在连续堆转储爬升的摘要中看到了 GC Roots #。 After two hours it showed 470,000 GC roots, after six hours, almost a million more GC roots, after 33 hours over 7 million GC roots.两个小时后,它显示了 470,000 个 GC 根,六个小时后,显示了近一百万个 GC 根,33 小时后显示了超过 700 万个 GC 根。

However, when I look at the Head Dump that says there are 7 million GC roots, and I choose the "Objects" view and the "GC Roots" preset, I select Aggregation "Types".但是,当我查看说有 700 万个 GC 根的 Head Dump 时,我选择了“对象”视图和“GC 根”预设,我选择了 select 聚合“类型”。 This list shows a total count of less than 15,000 objects:此列表显示的对象总数少于 15,000 个:

  • JNI global - count 7857 JNI 全局 - 计数 7857
  • JNI local - count 5 JNI 本地 - 计数 5
  • Java frame - count 983 Java 帧 - 计数 983
  • monitor used - count 7使用的显示器 - 计数 7
  • sticky class - count 3596粘性 class - 计数 3596
  • thread object - count 145线程 object - 计数 145

Where are those 7 million roots?那七百万个根在哪里?

Different GC roots can reffer to the same object instance.不同的 GC 根可以引用同一个 object 实例。 This explains the difference, since count from "GC root" view display number of unique instances.这解释了差异,因为从“GC root”视图计数显示的唯一实例数。 You can find more details using OQL.您可以使用 OQL 找到更多详细信息。 First let's display number of GC roots for particular GC type:首先让我们显示特定 GC 类型的 GC 根数:

printHistogram()

function printHistogram() {
  var roots = heap.roots()
  var histoMap = new Array();
  var result = new Array();
  var cnt = 0;
 
  while (roots.hasMoreElements()) {
    var root = roots.nextElement();
    var type = root.type;
    if (histoMap[type] == undefined) {
      histoMap[type] = 1;
    } else {
      histoMap[type]++;
    }
  }
  for (var key in histoMap){
    if (histoMap.hasOwnProperty(key)) {
      result[cnt++] = { key: key, count: histoMap[key] };
  }
  return map(sort(result, "rhs.count - lhs.count"), '"Root count: "+it.count+" for type: "+it.key');
}

Running this query against your heap dump produces:针对您的堆转储运行此查询会产生:

Root count: 12878043 for type: JNI local
Root count: 7858 for type: JNI global
Root count: 3599 for type: sticky class
Root count: 1631 for type: Java frame
Root count: 146 for type: thread object
Root count: 7 for type: monitor used

We can see that the majority of GC roots are of "JNI local" type.我们可以看到大多数 GC 根是“JNI 本地”类型。 Let us see how many "JNI local" roots point to the same object instance.让我们看看有多少“JNI 本地”根指向同一个 object 实例。 We can modify above query to:我们可以将上面的查询修改为:

printHistogram()

function printHistogram() {
  var roots = heap.roots()
  var histoMap = new Array();
  var result = new Array();
  var cnt = 0;

  while (roots.hasMoreElements()) {
    var root = roots.nextElement();
    if (root.type == "JNI local") {
      var objid = root.id;
      if (histoMap[objid] == undefined) {
        histoMap[objid] = 1;
      } else {
        histoMap[objid]++;
      }
    }
  }
  for (var key in histoMap){
    if (histoMap.hasOwnProperty(key)) {
      result[cnt++] = { key: key, count: histoMap[key] };
  }
}

return map(sort(result, "rhs.count - lhs.count"), '"Root count: "+it.count+" for object: "+toHtml(heap.findObject(it.key))');

} }

The result is below:结果如下:

Root count: 6439020 for object: java.lang.String#44429
Root count: 6439020 for object: java.lang.String#55081
Root count: 1 for object: java.nio.DirectByteBuffer#9
Root count: 1 for object: java.util.ArrayList#22281
Root count: 1 for object: java.lang.String#71518

We can see that two strings java.lang.String#44429 and java.lang.String#55081 are responsible for that huge number of GC roots.我们可以看到两个字符串java.lang.String#44429java.lang.String#55081负责大量的 GC 根。 They have 6,5 million GC roots each.他们每个都有 650 万个 GC 根。

Those "JNI local" GC root are referenced from MessageDispatherThread-1 (tid=216) from frame ca.digitalrapids.kayak.jni.KayakNativeWorkerThread.runNative (Native Method) .那些“JNI 本地”GC 根是从框架ca.digitalrapids.kayak.jni.KayakNativeWorkerThread.runNative (Native Method)MessageDispatherThread-1 (tid=216)引用的。 See screenshots below:请参阅下面的屏幕截图:

堆栈跟踪

当地人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM