ObjectHeap.iterateObjectsOfKlass
(在 SA 的帮助下)来获取属于某个类的所有对象。结果正是我所期望的,但性能却不是。
我花了超过 800 秒才得到结果,在此期间目标虚拟机被挂起。目标VM堆约为2GB。我知道
iterateObjectsOfKlass
会打电话给iterateExact
。
我的问题是:这些方法是否迭代/遍历整个堆只是为了获取 1 个类的对象?我很失望,因为我的期望是对于单个类,结果应该在 10 秒内返回。
HotSpot Serviceability Agent 确实是强大的技术,但确实很慢。我已经在这个答案中解释了它是如何工作的。
JVM 无法快速找到特定类的所有实例。所以,是的,它必须扫描整个堆。此外,为了读取外部进程的内存,SA 对每个数据字使用
ptrace
系统调用。这就是为什么它这么慢。
您有多种选项可以更快地扫描堆:
IterateOverInstancesOfClass
函数扫描本地 JVM 的堆。与 SA 相比,这会快得多,因为它只是从同一进程中读取,而不需要任何系统调用或其他任何东西。我相信 2GB 的堆只需要几秒钟。我在 JDK17 上尝试使用 coredump 进行 SA。看起来和attach pid一样慢,因为有LRU页面缓存。
"pool-1-thread-1" #25 prio=5 os_prio=0 cpu=1881.17ms elapsed=29538435.51s tid=0x00007f51dab59a00 nid=0x11a6f waiting for monitor entry [0x00007f51d479b000]
java.lang.Thread.State: BLOCKED (on object monitor)
at sun.jvm.hotspot.debugger.PageCache.getData([email protected]/PageCache.java:57)
- waiting to lock <0x0000000530000000> (a sun.jvm.hotspot.debugger.PageCache)
at sun.jvm.hotspot.debugger.DebuggerBase.readBytes([email protected]/DebuggerBase.java:225)
at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal.readCInteger([email protected]/LinuxDebuggerLocal.java:565)
at sun.jvm.hotspot.debugger.DebuggerBase.readCompKlassAddressValue([email protected]/DebuggerBase.java:477)
at sun.jvm.hotspot.debugger.linux.LinuxDebuggerLocal.readCompKlassAddress([email protected]/LinuxDebuggerLocal.java:511)
at sun.jvm.hotspot.debugger.linux.LinuxAddress.getCompKlassAddressAt([email protected]/LinuxAddress.java:83)
at sun.jvm.hotspot.oops.NarrowKlassField.getValue([email protected]/NarrowKlassField.java:36)
at sun.jvm.hotspot.oops.Oop.getKlass([email protected]/Oop.java:82)
at sun.jvm.hotspot.oops.Oop.getObjectSize([email protected]/Oop.java:94)
at com.kuaishou.alwayson.profiler.sa.oops.G1GenAccessor.lambda$processLiveRegion$1(G1GenAccessor.java:130)
at com.kuaishou.alwayson.profiler.sa.oops.G1GenAccessor$$Lambda$51/0x0000000801097490.run(Unknown Source)
at java.util.concurrent.Executors$RunnableAdapter.call([email protected]/Executors.java:539)
at java.util.concurrent.FutureTask.run([email protected]/FutureTask.java:264)
at java.util.concurrent.ThreadPoolExecutor.runWorker([email protected]/ThreadPoolExecutor.java:1136)
at java.util.concurrent.ThreadPoolExecutor$Worker.run([email protected]/ThreadPoolExecutor.java:635)
at java.lang.Thread.run([email protected]/Thread.java:959)