了解Java堆转储

时间:2011-07-20 01:03:28

标签: java memory memory-leaks heap dump

我一直试图在我的应用程序中找到内存泄漏一周,但没有任何成功。我尝试进行堆转储并使用jhat查看转储并追踪内存泄漏。

这是最好的方法吗?什么是使用堆转储来追踪内存泄漏的最佳方法。

感谢您的帮助。

使用的VM: java版“1.6.0_25” Java(TM)SE运行时环境(版本1.6.0_25-b06) Java HotSpot(TM)64位服务器VM(内置20.0-b11,混合模式)

JVM选项: -Xmx1600m -XX:+ UseParallelGC -XX:MaxPermSize = 256m -Xms1600m -XX:+ HeapDumpOnOutOfMemoryError -XX:+ PrintGCDetails -XX:+ PrintGCTimeStamps -verbose:gc -Xloggc: /tmp/gc.log

OOME堆栈跟踪:无法获取此信息。内核因内存不足错误而终止进程。

GC日志:最后几行

48587.245: [GC [PSYoungGen: 407168K->37504K(476160K)] 506729K->137065K(1568448K), 3.0673560 secs] [Times: user=3.53 sys=0.00, real=3.07 secs] 
50318.617: [GC [PSYoungGen: 444224K->37536K(476416K)] 543785K->175177K(1568704K), 3.6635990 secs] [Times: user=3.70 sys=0.00, real=3.67 secs] 
50453.841: [GC [PSYoungGen: 70092K->2912K(476672K)] 207734K->178513K(1568960K), 1.0164250 secs] [Times: user=1.29 sys=0.00, real=1.02 secs] 
50454.858: [Full GC (System) [PSYoungGen: 2912K->0K(476672K)] [PSOldGen: 175601K->137776K(1092288K)] 178513K->137776K(1568960K) [PSPermGen: 60627K->60627K(74368K)], 2.0082140 secs] [Times: user=2.09 sys=0.00, real=2.01 secs] 
52186.496: [GC [PSYoungGen: 407104K->37312K(444416K)] 544880K->175088K(1536704K), 3.3705440 secs] [Times: user=3.93 sys=0.00, real=3.37 secs] 
53919.975: [GC [PSYoungGen: 444416K->37536K(476608K)] 582192K->213032K(1568896K), 3.4242980 secs] [Times: user=4.09 sys=0.00, real=3.42 secs] 
54056.872: [GC [PSYoungGen: 70113K->2880K(476480K)] 245609K->216320K(1568768K), 0.9691980 secs] [Times: user=1.19 sys=0.00, real=0.97 secs] 
54057.842: [Full GC (System) [PSYoungGen: 2880K->0K(476480K)] [PSOldGen: 213440K->99561K(1092288K)] 216320K->99561K(1568768K) [PSPermGen: 60628K->60628K(72320K)], 2.2203320 secs] [Times: user=2.23 sys=0.01, real=2.22 secs] 
55796.688: [GC [PSYoungGen: 406976K->37504K(476160K)] 506537K->137065K(1568448K), 3.2680080 secs]

更新:检查内核日志消息后,它就是一个杀手锏。但仍然为什么系统会杀死进程,不是因为进程占用了大量的系统资源(内存)。

3 个答案:

答案 0 :(得分:8)

关于java内存泄漏的问题是thisthat等的重复。不过,这里有一些想法:

首先按照上面链接的答案中的描述拍摄一些堆快照。

然后,如果您对整个应用程序了解得很清楚,您可以关注实例计数并查找哪种类型的实例太多了。例如,如果你知道一个类是一个单例,但你在内存中看到该类的100个实例,那么这肯定表明那里有一些有趣的东西。或者,您可以比较快照以查找哪些类型的对象随着时间的推移而增长;这里的关键是你在一段时间内寻找相对增长。

一旦你知道泄漏了什么,你就会追溯参考文献以找到无法收集的根参考。

最后,请记住,您可能看到OutOfMemoryError不是因为您正在泄漏内存,而是因为您的堆的某些部分对于应用程序而言太小。要检查是否是这种情况:

  • 在您的问题中包含您正在使用的虚拟机类型。
  • 在您的问题中包含您在启动java时传递的参数。你的min,max和permgen堆大小是多少?你使用什么类型的垃圾收集器?
  • 在您的问题中包含OOME堆栈跟踪,以防有一些有用的信息。
  • 打开详细的GC日志记录,以便您可以看到堆的哪个部分正在增长。
  • 启用HeapDumpOnOutOfMemoryError parameter,以便在进程终止时在最后获得堆转储。

更新:我不确定你的最新更新意味着什么“内核会因为内存不足错误而导致进程失效”,但我想你可能会说linux out of memory killer是调用。是这样的吗?此问题与java OutOfMemoryError完全分开。有关正在发生的事情的详细信息,请查看我刚刚链接到的页面中的链接,包括thisthat。但是问题的解决方案很简单:在服务器上使用更少的内存。我想您可以删除有问题的java进程的最小和最大堆大小,但是您需要确保不会触发真正的Java OutOfMemoryErrors。你能在其他地方移动一些流程吗你能否将记忆杀手与特定过程的启动联系起来?

答案 1 :(得分:1)

内存泄漏可以通过以下3个简单步骤解决:

步骤1:在早期阶段捕获堆转储

启动您的申请。让它需要10分钟的真实流量。此时捕获堆转储。堆转储基本上是你记忆的快照。它包含驻留在内存中的所有对象,存储在这些对象中的值,入站和放大器。这些对象的出站引用。您可以使用以下命令捕获堆转储:

   jmap -dump:format=b,file=<file-path> <pid> 

   where

   pid: is the Java Process Id, whose heap dump should be captured
   file-path: is the file path where heap dump will be written in to.

如果您不想使用jmap捕获堆转储,以下是capture heap dumps的其他几个选项。

步骤2:在应用程序崩溃之前捕获堆转储

执行步骤#1后,让应用程序运行。在应用程序崩溃之前再次进行另一个堆转储。通常,在崩溃之前捕获堆转储可能具有挑战性,因为我们不知道应用程序何时崩溃。是30分钟,3小时,3天后?因此,使用以下JVM属性启动应用程序是理想的:

    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=<file-path>

    file-path: is the file path where heap dump will be written in to.

当应用程序遇到OutOfMemoryError时,此属性将触发堆转储。

第3步:分析堆转储

导致内存泄漏的对象在此期间会增长。如果能够识别在步骤#1和步骤#2中捕获的堆转储之间大小增加的对象,那么这些是导致内存泄漏的对象。

为此,您可以考虑使用堆转储分析工具,例如HeapHero.ioEclipse MAT。将堆转储装入任何工具时,会有一个报告内存中最大对象的部分。在步骤#1和步骤#2中捕获的堆转储之间比较此部分。如果您发现对象有任何异常增长,那么它们就是导致应用程序内存泄漏的对象。

答案 2 :(得分:0)

我建议您查看有关此主题的原始文章。 Java堆转储分析最初可能很复杂,但使用Eclipse Memory Analyzer等工具可以简化流程。

JVM Heap Dump对以下场景非常有用:

  • 识别Java级内存泄漏。
  • 识别Java级别的类加载器泄漏。
  • 了解有关特定负载/流量情况下Java应用程序内存占用的更多信息。

参考:Java heap dump: are you up to the task?