【发布时间】:2020-04-18 20:24:05
【问题描述】:
我有一些代码会引发OutOfMemoryError。
我将 JVM 设置为在 OOM 上转储,并在 Java Flight Recorder 中打开转储。
在 JFR 中检查活动对象时,我看到的对象很少(少于 60 个)。
在触发 OOM 的那一刻,我如何找出内存中保存且不可回收的最大对象?
【问题讨论】:
标签: java out-of-memory performance-testing
我有一些代码会引发OutOfMemoryError。
我将 JVM 设置为在 OOM 上转储,并在 Java Flight Recorder 中打开转储。
在 JFR 中检查活动对象时,我看到的对象很少(少于 60 个)。
在触发 OOM 的那一刻,我如何找出内存中保存且不可回收的最大对象?
【问题讨论】:
标签: java out-of-memory performance-testing
对象是采样的,因此无法确保在 OOM 之前看到最大的对象。
也就是说,60 个样本通常足以发现内存泄漏,至少如果应用程序已经运行了一段时间并且泄漏的大小不可忽略。
一开始发生的示例通常是您在整个应用程序期间拥有的单例和静态对象。最后发生的样本通常是要被垃圾收集的短期对象。在 JMC 中,您可以单击顶部时间线的“中间”以查找更多泄漏候选者。然后,您可以查看堆栈跟踪和 GC 根路径,看看是否有可疑之处。
你也可以使用命令行工具做:
$ jfr print --events OldObjectSample --stack-depth 64 recording.jfr
它将按时间顺序列出样本。查看每个样本可能比查看聚合更容易。命令行方式详解here
【讨论】:
由于所收集数据的性质,您无法以自动方式执行此操作(例如内存分析工具使用堆转储执行此操作)。
你只能看到少数物体是完全可以的。原因是低开销采样的工作原理 - 在每个新的 TLAB 分配 JFR 介入并从旧 TLAB 中获取一些对象。因此,您不会记录所有对象,而只会分配具有代表性的对象样本。这应该足以为您提供堆中对象的比率。此外,所有报告的对象在记录转储时都是活动的。
如果您认为获得的样本太少而无法得出正确的结论,则可能是您的堆相对于 TLAB 大小较小,您可能希望减小 TLAB 大小。这在生产环境中是不可取的,因为不正确的 TLAB 设置会降低应用程序的性能。
如果您在记录期间在 profiling 配置中设置了“内存泄漏检测”到“对象类型 + 分配堆栈跟踪 + GC 根路径”,您可以在创建后跟踪活动对象在代码中的位置,您可以重建代表支配树就是这样。
如果您关心大对象本身就意味着大(并且不保留大部分堆),您可以通过查看“TLAB 分配”页面并查找“TLAB 之外的总分配”列来找到大于 TLAB 的对象。仅当分析配置将“内存分析”设置为“对象分配和提升”时才会收集此数据。
通过分析配置,我指的是您在开始使用 JFR 录制时使用 settings 选项指定的文件。此文件可以使用 JMC 应用程序的“飞行记录模板管理器”创建。
【讨论】: