Java:如何在不使用jmap或挂起应用程序的情况下获取堆转储

11
在某些情况下,我们的应用程序使用了大约12 GB的内存。我们尝试使用jmap实用程序获取堆转储。由于应用程序使用了一些GB的内存,它会导致应用程序停止响应并在生产中造成问题。
在我们的情况下,堆使用量突然从2-3 GB增加到12GB,需要在6小时内进行处理。为了找出内存使用趋势,我们尝试在重新启动应用程序后每隔一小时收集一次堆转储。但是,由于使用jmap会导致应用程序挂起,因此我们需要重启它,无法获取内存使用趋势。
是否有一种方法可以在不挂起应用程序的情况下获取堆转储,或者是否有其他工具可用于收集堆转储。
非常感谢您对此的建议,因为如果不能获取内存使用趋势,则很难修复此问题。
注意:我们的应用程序在CentOS上运行。
谢谢, Arun
5个回答

12

请尝试以下操作,需要使用 JDK >= 7:

/usr/lib/jvm/jdk-YOUR-VERSION/bin/jcmd PID GC.heap_dump FILE-PATH-TO-SAVE

示例:

/usr/lib/jvm/jdk1.8.0_91/bin/jcmd 25092 GC.heap_dump /opt/hd/3-19.11-jcmd.hprof

这个转储过程比使用jmap转储要快得多!转储文件要小得多,但足以让您知道泄漏的位置。

在撰写本答案时,Memory Analyzer和IBM HeapAnalyzer存在错误,它们无法读取来自jmap(jdk8、大文件)的转储文件。您可以使用Yourkit来读取这些文件。


2
首先,冻结JVM在获取线程转储/快照时是必要的(据我所知)。如果JVM能够在创建快照时继续运行,那么几乎不可能获得一致的快照。
那么还有其他获取堆转储的方法吗?
- 您可以使用VisualVM获取堆转储,如此处所述。 - 您可以使用jconsole或Eclipse Memory Analyser获取堆转储,如此处所述。
但是所有这些方法都会导致JVM(至少)暂停。
如果您的应用程序实际上被挂起(永久性!),那么这听起来像是应用程序本身的问题。我的建议是,在寻找存储泄漏之前,看看是否可以追踪到该问题。
我的另一个建议是查看单个堆转储,并使用统计信息确定使用所有空间的对象类型和原因...以及它们为什么是可达的。很有可能您根本不需要“趋势”信息。

感谢Stephen。你是在建议使用直方图吗? - Arun
1
@Arun - 我建议你先获取一个堆转储文件,然后使用可用工具来进行以下操作:1)识别最大、最常见或最可疑的对象类型,然后2)找出它们是如何被引用的以及为什么会被引用。这是在Java中寻找存储泄漏的正常方法。 - Stephen C

2
您可以使用GDB在不运行目标VM上获取堆转储,但这仍会使应用程序挂起,直到将堆转储写入磁盘所需的时间。假设磁盘速度为100MB/s(基本镜像阵列或单个磁盘),这仍然需要2分钟的停机时间。 唯一真正避免停止JVM的方法是事务性内存和利用它提供进程快照功能的内核。这是STM支持者的梦想之一,但目前还没有可用的解决方案。 VMWare的热迁移接近此目标,但取决于您的分配速率是否超过网络带宽,并且它不保存快照。请向他们申请添加此功能,这将是一个很棒的特性。 http://blogs.atlassian.com/2013/03/so-you-want-your-jvms-heap/

1
通过合适的工具分析堆转储文件,可以精确地查找消耗堆内存的原因。它是追踪内存泄漏的最佳工具。然而,收集堆转储文件很慢,更不用说分析了。
如果您了解应用程序的工作原理,有时直方图就足以给您提供在哪里寻找问题的线索。例如,如果 MyClass$Inner 在直方图的顶部,并且 MyClass$Inner 仅在 MyClass 中使用,则您知道要查找哪个文件的问题。
这是收集直方图的命令:jcmdpidGC.class_histogram filename=histogram.txt

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接