最好的调试方法是什么 java.lang.OutOfMemoryError 例外情况?

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


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

有帮助吗?

解决方案

在Java中分析和修复了内存错误非常简单。

在Java中,占据内存的对象都链接到其他某些对象,形成巨型树。这个想法是找到树的最大分支,这通常会指向内存泄漏情况(在Java中,当您忘记删除对象时,您的内存不是泄漏内存,但是当您忘记忘记对象时,即您保留一个在某个地方引用它)。

步骤1.在运行时启用堆转储

运行您的过程 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp

(可以始终启用这些选项是安全的。根据需要调整路径,Java用户必须是可写的)

步骤2.复制错误

让应用程序运行直到 OutOfMemoryError 发生。

JVM将自动编写一个文件 java_pid12345.hprof.

步骤3.获取转储

复制 java_pid12345.hprof 到您的PC(至少与您的最大堆大小一样大,因此可以变得很大 - 必要时GZIP)。

步骤4.使用IBM打开转储文件 堆分析仪 或Eclipse的 内存分析仪

堆分析仪将向您展示在错误时生存的所有对象的树。有机会指向你 直接地 在问题打开时。

IBM HeapAnalyzer

注意:给Hepanalyzer足够的内存,因为它需要加载整个转储!

java -Xmx10g -jar ha456.jar

步骤5.确定最大堆的区域

浏览对象树并识别不必要的对象。

注意也可能发生 全部 对象是必要的,这意味着您需要更大的堆。大小和 适当.

步骤6.修复您的代码

确保仅保留您实际需要的对象。及时从收藏中删除项目。确保不要对不再需要的对象进行引用,只有然后才能收集垃圾。

其他提示

我成功地结合了 日食记忆分析仪(MAT)Java Visual VM 分析堆转储。 MAT有一些报告,您可以运行,可以使您一般地了解将您的精力集中在代码中的位置。 VisualVM具有更好的界面(我认为),可以实际检查您有兴趣检查的各种对象的内容。它具有一个过滤器,您可以在其中显示特定类的所有实例,并查看它们的参考位置以及他们自己的参考。自从我使用任何一种工具以来,已经有一段时间了,他们现在可能设置了更紧密的功能。当时使用两者对我来说效果很好。

通常很难调试 OutOfMemoryError 问题。我建议使用分析工具。 Jprofiler运作良好。我过去曾经使用过它,并且可能非常有帮助,但是我敢肯定,至少有些其他人也一样。

回答您的特定问题:

堆转储是整个堆的完整视图,即使用的所有对象 new. 。如果您的内存不足,那么这将很大。它向您展示了您拥有的每种对象。

线程转储显示每个线程的堆栈,向您显示每个线程在转储时的位置。记住这一点 任何 线程可能会导致JVM用完内存,但实际上可能是抛出错误的另一个线程。例如,线程1分配一个填充所有可用堆空间的字节数组,然后Thread 2尝试分配1字节数组并引发错误。

您也可以使用JMAP/JHAT连接到运行的Java进程。如果您必须调试实时运行应用程序,这些(一组)工具真的很有用。

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

jmap -histo:live <pid> | head -n <top N things to look for> > <output.log>

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/

什么是最好的调试方法 java.lang.OutOfMemoryError 例外?

OutOfMemoryError 描述消息描述中的错误类型。您必须检查错误消息的描述才能处理异常。

有各种根源原因导致内存异常。请参阅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类Metadata(Java类的虚拟机内表现)分配在本机内存中(此处称为Metaspace)

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

原因: 详细消息“请求大小字节是有原因的,没有交换空间?”似乎是一个 OutOfMemoryError 例外。但是,当天然堆失败和本机堆可能接近耗尽时,Java热点VM代码报告了此明显的例外情况

java.lang.OutOfMemoryError: Compressed class space

原因: 在64位平台上,可以通过32位偏移量(使用UseCompressedoops)来表示元数据的指针。这是由命令行flag usecompressedClassPointers(默认情况下)控制的。

如果是 UseCompressedClassPointers 使用,元数据可用的空间数量为 CompressedClassSpaceSize. 。如果需要的空间 UseCompressedClassPointers 超过 CompressedClassSpaceSize, , 一个 java.lang.OutOfMemoryError 带有细节压缩的类空间。

笔记: 有一种以上的类元数据-Klass元数据和其他元数据。只有Klass Metadata存储在由 CompressedClassSpaceSize. 。另一个元数据存储在Metaspace中。

我们应该使用堆转储文件吗?我们应该生成一个Java线程转储吗?到底有什么区别?

是的。您可以使用此堆堆转储文件使用分析工具来调试问题 VisualVm 或者 您可以使用线程转储获得有关线程状态的进一步洞察力。

请参阅此问题以了解不同的问题:

Javacore,线程转储和堆转储之间的区别在WebSphere中

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

kill -3 <process_id> 生成线程转储,此命令不会杀死Java进程。

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

一旦获得了一个工具来查看堆转储,请查看线程堆栈中运行状态中的任何线程。它可能是那些有错误的人之一。有时,堆转储会告诉您哪个线程在顶部有错误。

那应该指向您正确的方向。然后采用标准调试技术(记录,调试器等)来磨练问题。使用运行时类获取当前的内存使用量并将其记录为所讨论的方法或过程中执行的方法。

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

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

正如其他人提到的,捕获线程转储的成本并不高,但捕获堆转储的成本却很高。我观察到,在捕获堆转储应用程序时(通常)会冻结,只有杀死然后重新启动才能帮助恢复。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top