如何理解“新”Java 飞行记录器 ObjectAllocationSample 事件?
Posted
技术标签:
【中文标题】如何理解“新”Java 飞行记录器 ObjectAllocationSample 事件?【英文标题】:How to make sense of the "new" Java flight recorder ObjectAllocationSample event? 【发布时间】:2021-10-26 06:37:06 【问题描述】:在 OpenJDK 16.1 上分析我们的应用程序时,我看到 ObjectAllocationSample 事件中记录了一些奇怪的分配样本。
第一个示例事件是这个最初让我有点害怕的事件:
jdk.ObjectAllocationSample
startTime = 22:22:12.027
objectClass = java.util.ArrayList (classLoader = bootstrap)
weight = 6.1 GB
eventThread = "My thread" (javaThreadId = 52)
stackTrace = [
jdk.jfr.internal.PlatformRecorder.getListeners() line: 169
jdk.jfr.internal.PlatformRecording.notifyIfStateChanged(RecordingState, RecordingState) line: 496
jdk.jfr.internal.PlatformRecording.start() line: 150
jdk.jfr.Recording.start() line: 184
myapp.JfrProfiler.start() line: 152
...
]
我很难相信 JFR 侦听器的列表有 6.1GB 大,并且该方法只调用了一次,那么在这种情况下,事件中的堆栈跟踪是什么意思呢? 这只是我们分配 ArrayList 的调用点之一,而且它恰好是开始录制后的第一个调用点吗? 如果是,是否在开始记录之前权重包括所有分配?
相同objectClass
的后续样本呢?我们能否相信堆栈跟踪可以很好地指示分配发生的位置,还是只是分配发生的随机位置?
诸如 JProfiler 之类的工具使用堆栈跟踪来提示哪些方法是分配热点,从而使上述调用站点显着突出,即使它不是大多数分配发生的地方。
【问题讨论】:
【参考方案1】:该字段代表样本重量,而不是大小。
如果您要运行应用程序很长时间,这就是该调用站点/堆栈跟踪对分配压力的贡献程度。
为了保持低开销,采样器不会检测每个分配,在这种情况下发出的大小是合理的。
相反,它依赖于 JVM 内部的分配方式。为了避免一直使用“堆锁”,分配是在线程本地分配缓冲区 (TLAB) 中完成的。 TLAB 的大小由 JVM 确定,但通常会随着分配的增加而增加。它可以是几千字节到几兆字节。当线程用完 TLAB 时,它会进入一条缓慢的路径,在该路径中请求更多内存,可能会触发垃圾收集。此时,JVM 决定是否应该采样。
TLAB 的大小决定了样品的重量。如果很少分配某种类型的对象,则不太可能达到 TLAB 阈值。如果它经常分配,它很可能会达到采样点。此外,可以在 TLAB 之外分配大型数组,然后权重变为数组的大小。为了保持较低的开销,每秒发出多少个事件也有限制,默认为 150,因此权重可以是多次采样尝试的总和。
也就是说,6.1 GB 听起来相当高。对于每秒 150 个事件,这意味着大约 1000 GB/s 的分配速率。该事件是 JDK 16 的新事件,因此可能是错误、未初始化的内存或其他原因。您似乎也不太可能以如此高的分配率碰到只分配一次的对象。
【讨论】:
以上是关于如何理解“新”Java 飞行记录器 ObjectAllocationSample 事件?的主要内容,如果未能解决你的问题,请参考以下文章
ObjectA.Signal.connect(ObjectB.Slot) 不工作。我的理解正确吗?
读Java性能权威指南(第2版)笔记24_ Java飞行记录器JFR
Java Mission Control:飞行记录器抛出:javax.naming.ServiceUnavailableException