如果应用程序在 GC 中,如何可靠地获取 JVM 核心转储?

Posted

技术标签:

【中文标题】如果应用程序在 GC 中,如何可靠地获取 JVM 核心转储?【英文标题】:How to reliably take JVM core dump if application is in GC? 【发布时间】:2012-12-18 13:56:28 【问题描述】:

我正在尝试查找我的 java 应用程序中内存泄漏的原因。我需要为处于 long GC 周期中的进程获取堆转储。 Jmap 在这种情况下不起作用,因为应用程序挂起,而且堆非常大。

不幸的是,jmap 在我获取的核心转储中抛出 UnknownOopException。我认为在 GC 期间进行核心转储是不正确的。有什么方法可以在正确进行核心转储时暂停 java 进程?

或者我完全错了,因为其他问题导致核心转储损坏?

【问题讨论】:

【参考方案1】:

在执行 GC 时不能进行堆转储。您需要在 GC 之前或之后进行堆转储。如果您想知道为什么要花这么长时间,那么确定哪个 pahse 需要这么长时间是很有用的。看到这个添加-verbosegc这将表明是否需要很长时间才能到达安全点、复制对象、扫描永久空间、检查引用或其他内容。

这可能需要一些时间,因为您要清理的对象很多。作为一种猜测,最坏的情况下每 2 GB 堆对象可能需要 1 秒。

【讨论】:

是否可以在安全点强制挂起 JVM 以安全地进行核心转储? 对于来到这里的人:在实践中,我发现在使用具有 30 GB 堆的 CMS 时,每 2 GB 堆对象所用的时间比 1 秒差(它可能需要几分钟才能生存)。 @pickypg 你是对的,根据你的使用情况,你可以得到更糟糕的时间。即,您是否有很多从旧代到新一代的引用、很多引用或很多 finalise()able 对象。如果您避免使用速度较慢的功能,但是对于 80 GB 堆(我正在尝试调整的应用程序;),在最坏的情况下,您可以获得 4 秒的暂停时间;)【参考方案2】:

您需要做的是在堆接近满以致 GC 锁定应用程序之前进行堆转储。

【讨论】:

即使我在 GC 锁定应用程序之前获得核心转储,我也无法确定核心转储是否一致。 我想你可以。我的理解是,当堆被转储时,JVM 会暂停所有线程。 可能在 JVM 进程上直接使用 jmap 时会这样做。但是 jmap 在大堆上的表现非常糟糕,所以我必须使用 gcore。 很遗憾这是不可能的【参考方案3】:

根据我的经验,OutOfMemory 异常或较长的 GC 周期并不一定表示内存泄漏。

为了搜索内存泄漏,需要间隔一段时间进行 2 个单独的堆转储(我使用过 jvisualvm,现在一个版本与 JDK 捆绑在一起)并分析它们。提示:检查对象的保留大小会有所帮助。

根据您的应用程序所做的事情以及如果没有出现明显的内存泄漏,调整 JVM GC 选项是您最好的选择。寻找世代比率,新对象被终身使用后的世代等。

希望这会有所帮助。

【讨论】:

以上是关于如果应用程序在 GC 中,如何可靠地获取 JVM 核心转储?的主要内容,如果未能解决你的问题,请参考以下文章

如果 JVM 在进行 GC 时不断移动对象,它如何解析引用?

JVM的四种GC算法

如何设置jvm启动参数

JVM 优化

JVM-GC设计思路分析

JVM系列第14讲:JVM参数之GC日志配置