1

我发现了大量关于“保留大小”的问题,接受的答案似乎是:

对象的保留大小是该对象从垃圾收集中保留的内存量。

现在,我一直在使用 Netbeans 分析器库(在 中完成保留大小计算)对文件中的保留大小(如此处定义)进行编程hprof计算。工作得很好(抱歉,为简洁起见,使用了 kotlin):HprofHeap.java

val heap: Heap = HeapFactory.createHeap(myHeap.toFile())
val threadClass: JavaClass = heap.getJavaClassByName("java.lang.Thread")
val instanceFilter = { it: Instance -> threadClass == it.getJavaClass() }
val sizeMap = heap.allInstances
            .filter { instanceFilter(it) }
            .toMap({ findThreadName(it) /* not shown */ }, { it.retainedSize })

sizeMap当保留大小只有边际数量时,我注意到的是 Netbeans 仅计算不在堆栈上的对象的保留大小。因此分配给 的局部变量(在堆栈上分配)Thread不会包含在保留大小中

我的问题是:有没有办法让 netbeans 库将堆栈元素视为依赖对象,例如 Yourkit Profiler 的计算方式?如果上一个问题的答案是“否”,我将如何添加这样的功能?

4

1 回答 1

2

一点挖掘发现JVM堆转储器ROOT JAVA FRAME为堆栈局部变量创建了一个类型的条目(比较VM_HeapDumper::do_thread)。由于我可以在堆中 grep ,这就是我所做的:

val threadClass: JavaClass = heap.getJavaClassByName("java.lang.Thread")

val keyTransformer = { it: Instance -> findThreadName(it) }
val instanceFilter = { it: Instance -> it.getJavaClass() == threadClass }

val stackLocals = heap.gcRoots
        .filter { it.kind == GCRoot.JAVA_FRAME }
        .groupBy { (it as JavaFrameGCRoot).threadGCRoot }

val sizeMap = heap.allInstances
      .filter { instanceFilter(it) }
      .toMap(
          { keyTransformer(it) }, 
          { 
              val locals = stackLocals[heap.getGCRoot(it)]
              val localSize = locals!!.sumBy { it.instance.retainedSize.toInt() }
              it.retainedSize + localSize
          })

return Report(
        sizeMap.values.sum(),
        sizeMap.keys.size.toLong(),
        sizeMap.maxBy { it.value }?.let { it.toPair() } ?: ("n/a" to 0L))

This solution is based on finding the GC root for each thread (should be the Thread itself), then sort to the stored gc root of the JAVA FRAME (the thread [= GC root] id is part of the stored entry data).

There's still a slight difference compared to the values from Yourkit, probably due to me missing ROOT JNI LOCAL entities, but it's close enough for me.

于 2015-12-18T13:40:05.577 回答