如何调试Java OutOfMemoryexception?

调试java.lang.OutOfMemoryErrorexception的最佳方法是什么?

当我们的应用程序发生这种情况时,我们的应用服务器(Weblogic)会生成堆转储文件。 我们应该使用堆转储文件吗? 我们应该生成Java线程转储吗? 究竟有什么区别?


更新:生成线程转储的最佳方法是什么? kill -3 (我们的应用程序在Solaris上运行)是杀死应用程序并生成线程转储的最佳方法吗? 有没有办法生成线程转储但不杀死应用程序?

我已经成功地使用Eclipse Memory Analyzer(MAT)和Java Visual VM的组合来分析堆转储。 MAT提供了一些可以运行的报告,可以让您大致了解在代码中将工作重点放在哪里。 VisualVM有一个更好的界面(在我看来)实际检查你有兴趣检查的各种对象的内容。 它有一个filter,您可以在其中显示特定类的所有实例,并查看它们的引用位置以及它们自己引用的内容。 已经有一段时间了,因为我使用了这两种工具,现在它们可能有更接近的function集。 当时使用两者对我来说都很好。

调试OutOfMemoryError问题通常非常困难。 我建议使用分析工具。 JProfiler工作得很好。 我过去曾经使用它,它可能非常有用,但我确信还有其他一些至少同样好的。

回答您的具体问题:

堆转储是整个堆的完整视图,即使用new创建的所有对象。 如果你的内存不足那么这将是相当大的。 它显示了您拥有的每种类型对象的数量。

线程转储显示每个线程的堆栈,显示转储时每个线程在代码中的位置。 请记住, 任何线程都可能导致JVM内存不足,但它可能是一个实际抛出错误的不同线程。 例如,线程1分配一个填满所有可用堆空间的字节数组,然后线程2尝试分配一个1字节的数组并引发错误。

您还可以使用jmap / jhat附加到正在运行的Java进程。 如果您必须调试实时运行的应用程序,这些(系列)工具非常有用。

您还可以将jmap作为cron任务登录到一个文件中运行,您可以在以后进行分析(这是我们发现调试实时内存泄漏有用的东西)

 jmap -histo:live  | head -n  >  

Jmap也可以用于使用-dump选项生成堆转储,该选项可以通过jhat读取。

有关详细信息,请参阅以下链接http://www.lshift.net/blog/2006/03/08/java-memory-profiling-with-jmap-and-jhat

这是书签http://java.sun.com/developer/technicalArticles/J2SE/monitoring/的另一个链接

看起来IBM提供了一种分析这些堆转储的工具: http : //www.alphaworks.ibm.com/tech/heaproots ; 更多信息, 请访问http://www-01.ibm.com/support/docview.wss?uid=swg21190476 。

调试java.lang.OutOfMemoryErrorexception的最佳方法是什么?

OutOfMemoryError描述消息描述中的错误类型。 您必须检查错误消息的描述以处理exception。

内存不足exception有多种根本原因。 有关更多详细信息,请参阅oracle文档页面 。

java.lang.OutOfMemoryError: Java heap space

原因 :详细消息Java堆空间表示无法在Java堆中分配对象。

java.lang.OutOfMemoryError: GC Overhead limit exceeded

原因:详细消息“超出GC开销限制”表示垃圾收集器一直在运行,Java程序进展非常缓慢

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

原因 :详细消息“请求的数组大小超过VM限制”表示应用程序(或该应用程序使用的API)尝试分配大于堆大小的数组。

java.lang.OutOfMemoryError: Metaspace

原因: Java类元数据(Java类的虚拟机内部表示)在本机内存中分配(此处称为元空间)

java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?

原因:详细消息“请求大小字节的原因。超出交换空间?” 似乎是OutOfMemoryErrorexception。 但是,当来自本机堆的分配失败并且本机堆可能接近耗尽时,Java HotSpot VM代码会报告此明显exception

java.lang.OutOfMemoryError: Compressed class space

原因:在64位平台上,指向类元数据的指针可以用32位偏移量表示(使用UseCompressedOops)。 这由命令行标志UseCompressedClassPointers(默认情况下为on)控制。

如果使用UseCompressedClassPointers ,则可用于类元数据的空间量固定为CompressedClassSpaceSize 。 如果UseCompressedClassPointers所需的空间超出CompressedClassSpaceSize ,则抛出带有详细压缩类空间的java.lang.OutOfMemoryError

注意:有多种类元数据–klass元数据和其他元数据。 只有klass元数据存储在CompressedClassSpaceSize限定的空间中。 其他元数据存储在Metaspace中。

我们应该使用堆转储文件吗? 我们应该生成Java线程转储吗? 究竟有什么区别?

是。 您可以使用此堆堆转储文件来使用诸如visualvm或mat之类的分析工具来调试问题。您可以使用线程转储来进一步了解线程的状态。

请参阅此SE问题以了解不同之处:

Websphere中javacore,线程转储和堆转储之间的区别

生成线程转储的最佳方法是什么? kill -3(我们的应用程序在Solaris上运行)是杀死应用程序并生成线程转储的最佳方法吗? 有没有办法生成线程转储但不杀死应用程序?

kill -3 生成线程转储,此命令不会终止java进程。

获得查看堆转储的工具后,查看线程堆栈中处于Running状态的任何线程。 它可能是那些得到错误的人之一。 有时堆转储会告诉你哪个线程的错误在顶部。

这应该指向正确的方向。 然后使用标准调试技术(日志记录,调试器等)来磨练问题。 使用Runtime类获取当前内存使用情况,并将其记录为执行中的方法或进程。

我通常使用Eclipse Memory Analyzer。 它显示可疑的罪魁祸首(占据大部分堆转储的对象)和生成这些对象的不同调用层次结构。 一旦映射到那里,我们就可以回到代码并尝试了解代码路径中是否存在任何可能的内存泄漏。

但是,OOM​​并不总是意味着存在内存泄漏。 在稳定状态或负载期间,应用程序所需的内存始终可能在硬件/ VM中不可用。 例如,可能存在32位Java进程(最大内存使用~4GB),其中VM只有3 GB。 在这种情况下,最初应用程序可能运行正常,但是当内存要求接近3GB时可能会遇到OOM。

正如其他人所提到的,捕获线程转储并不昂贵,但捕获堆转储却是如此。 我观察到,虽然捕获堆转储应用程序(通常)冻结,只有一个kill然后重启有助于恢复。