如何调试 Java OutOfMemory 异常?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4512147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 06:41:53  来源:igfitidea点击:

How to debug Java OutOfMemory exceptions?

javaout-of-memorycoredumpjava1.4thread-dump

提问by Marcus Leon

What is the best way to debug java.lang.OutOfMemoryErrorexceptions?

调试java.lang.OutOfMemoryError异常的最佳方法是什么?

When this happens to our application, our app server (Weblogic) generates a heap dump file. Should we use the heap dump file? Should we generate a Java thread dump? What exactly is the difference?

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



Update: What is the best way to generate thread dumps? Is kill -3(our app runs on Solaris) the best way to kill the app and generate a thread dump? Is there a way to generate the thread dump but not kill the app?

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

采纳答案by rustyx

Analyzing and fixing out-of-memory errors in Java is very simple.

分析和修复 Java 中的内存不足错误非常简单。

In Java the objects that occupy memory are all linked to some other objects, forming a giant tree. The idea is to find the largest branches of the tree, which will usually point to a memory leak situation (in Java, you leak memory not when you forget to delete an object, but when you forget to forget the object, i.e. you keep a reference to it somewhere).

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

Step 1. Enable heap dumps at run time

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

Run your process with -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp

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

(It is safe to have these options always enabled. Adjust the path as needed, it must be writable by the java user)

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

Step 2. Reproduce the error

步骤 2. 重现错误

Let the application run until the OutOfMemoryErroroccurs.

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

The JVM will automatically write a file like java_pid12345.hprof.

JVM 会自动写入一个类似java_pid12345.hprof.

Step 3. Fetch the dump

步骤 3. 获取转储

Copy java_pid12345.hprofto your PC (it will be at least as big as your maximum heap size, so can get quite big - gzip it if necessary).

复制java_pid12345.hprof到您的 PC(它至少与您的最大堆大小一样大,因此可以变得非常大 - 如有必要,请对其进行 gzip)。

Step 4. Open the dump file with IBM's Heap Analyzeror Eclipse's Memory Analyzer

步骤 4. 使用 IBM 的Heap Analyzer或 Eclipse 的Memory Analyzer打开转储文件

The Heap Analyzer will present you with a tree of all objects that were alive at the time of the error. Chances are it will point you directlyat the problem when it opens.

堆分析器将向您显示发生错误时所有处于活动状态的对象的树。当它打开时,它可能会直接指向您的问题。

IBM HeapAnalyzer

IBM 堆分析器

Note: give HeapAnalyzer enough memory, since it needs to load your entire dump!

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

java -Xmx10g -jar ha456.jar

Step 5. Identify areas of largest heap use

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

Browse through the tree of objects and identify objects that are kept around unnecessarily.

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

Note it can also happen that allof the objects are necessary, which would mean you need a larger heap. Size and tunethe heap appropriately.

请注意,也可能发生所有对象都是必需的,这意味着您需要更大的堆。适当地调整调整堆的大小。

Step 6. Fix your code

步骤 6. 修复您的代码

Make sure to only keep objects around that you actually need. Remove items from collections in a timely manner. Make sure to not keep references to objects that are no longer needed, only then can they be garbage-collected.

确保只保留您实际需要的对象。及时从集合中删除项目。确保不要保留对不再需要的对象的引用,只有这样它们才能被垃圾收集。

回答by laz

I've had success using a combination of Eclipse Memory Analyzer (MAT)and Java Visual VMto analyze heap dumps. MAT has some reports that you can run that give you a general idea of where to focus your efforts within your code. VisualVM has a better interface (in my opinion) for actually inspecting the contents of the various objects that you are interested in examining. It has a filter where you can have it display all instances of a particular class and see where they are referenced and what they reference themselves. It has been a while since I've used either tool for this they may have a closer feature set now. At the time using both worked well for me.

我已经成功地结合使用Eclipse Memory Analyzer (MAT)Java Visual VM来分析堆转储。MAT 有一些您可以运行的报告,让您大致了解在代码中的工作重点。VisualVM 有一个更好的界面(在我看来),用于实际检查您有兴趣检查的各种对象的内容。它有一个过滤器,您可以在其中显示特定类的所有实例,并查看引用它们的位置以及它们自身引用的内容。自从我使用这两种工具以来已经有一段时间了,它们现在可能具有更接近的功能集。当时使用这两种方法对我来说效果很好。

回答by Ravindra babu

What is the best way to debug java.lang.OutOfMemoryErrorexceptions?

调试java.lang.OutOfMemoryError异常的最佳方法是什么?

The OutOfMemoryErrordescribes type of error in the message description. You have to check the description of the error message to handle the exception.

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

There are various root causes for out of memory exceptions. Refer to oracle documentation pagefor more details.

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

java.lang.OutOfMemoryError: Java heap space:

java.lang.OutOfMemoryError: Java heap space

Cause: The detail message Java heap space indicates object could not be allocated in the Java heap.

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

java.lang.OutOfMemoryError: GC Overhead limit exceeded:

java.lang.OutOfMemoryError: GC Overhead limit exceeded

Cause:The detail message "GC overhead limit exceeded" indicates that the garbage collector is running all the time and Java program is making very slow progress

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

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

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

Cause: The detail message "Requested array size exceeds VM limit" indicates that the application (or APIs used by that application) attempted to allocate an array that is larger than the heap size.

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

java.lang.OutOfMemoryError: Metaspace:

java.lang.OutOfMemoryError: Metaspace

Cause:Java class metadata (the virtual machines internal presentation of Java class) is allocated in native memory (referred to here as metaspace)

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

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

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

Cause:The detail message "request size bytes for reason. Out of swap space?" appears to be an OutOfMemoryErrorexception. However, the Java HotSpot VM code reports this apparent exception when an allocation from the native heap failed and the native heap might be close to exhaustion

原因:详细消息“出于原因请求大小字节。交换空间不足?” 似乎是个OutOfMemoryError例外。但是,当从本机堆分配失败并且本机堆可能接近耗尽时,Java HotSpot VM 代码会报告此明显异常

java.lang.OutOfMemoryError: Compressed class space

java.lang.OutOfMemoryError: Compressed class space

Cause:On 64-bit platforms a pointer to class metadata can be represented by a 32-bit offset (with UseCompressedOops). This is controlled by the command line flag UseCompressedClassPointers (on by default).

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

If the UseCompressedClassPointersis used, the amount of space available for class metadata is fixed at the amount CompressedClassSpaceSize. If the space needed for UseCompressedClassPointersexceeds CompressedClassSpaceSize, a java.lang.OutOfMemoryErrorwith detail Compressed class space is thrown.

如果UseCompressedClassPointers使用 ,则可用于类元数据的空间量固定为数量CompressedClassSpaceSize。如果需要的空间UseCompressedClassPointers超过CompressedClassSpaceSizejava.lang.OutOfMemoryError则会抛出一个带有详细信息的压缩类空间。

Note:There is more than one kind of class metadata - klass metadata and other metadata. Only klass metadata is stored in the space bounded by CompressedClassSpaceSize. The other metadata is stored in Metaspace.

注意:有不止一种类元数据——类元数据和其他元数据。只有 klass 元数据存储在由 限定的空间中CompressedClassSpaceSize。其他元数据存储在 Metaspace 中。

Should we use the heap dump file? Should we generate a Java thread dump? What exactly is the difference?

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

Yes. You can use this heap heap dump file to debug the issue using profiling tools like visualvmor matYou can use Thread dump to get further insight about status of threads.

是的。您可以使用此堆堆转储文件使用visualvmmat等分析工具调试问题 您可以使用线程转储进一步了解线程的状态。

Refer to this SE question to know the differenes:

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

Difference between javacore, thread dump and heap dump in Websphere

Websphere中javacore、线程转储和堆转储的区别

What is the best way to generate thread dumps? Is kill -3 (our app runs on Solaris) the best way to kill the app and generate a thread dump? Is there a way to generate the thread dump but not kill the app?

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

kill -3 <process_id>generates Thread dump and this command does not kill java process.

kill -3 <process_id>生成线程转储并且此命令不会终止 java 进程。

回答by Nauman

You can also use jmap/jhat to attach to a running Java process. These (family of) tools are really useful if you have to debug a live running application.

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

You can also leave jmap running as a cron task logging into a file which you can analyse later (It is something which we have found useful to debug a live memory leak)

您还可以让 jmap 作为 cron 任务运行,登录到您可以稍后分析的文件中(我们发现它对调试实时内存泄漏很有用)

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

Jmap can also be used to generate a heap dump using the -dump option which can be read through the jhat.

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

See the following link for more details http://www.lshift.net/blog/2006/03/08/java-memory-profiling-with-jmap-and-jhat

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

Here is another link to bookmark http://java.sun.com/developer/technicalArticles/J2SE/monitoring/

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

回答by Cameron Skinner

It is generally very difficult to debug OutOfMemoryErrorproblems. I'd recommend using a profiling tool. JProfiler works pretty well. I've used it in the past and it can be very helpful, but I'm sure there are others that are at least as good.

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

To answer your specific questions:

要回答您的具体问题:

A heap dump is a complete view of the entire heap, i.e. all objects that have been created with new. If you're running out of memory then this will be rather large. It shows you how many of each type of object you have.

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

A thread dump shows you the stack for each thread, showing you where in the code each thread is at the time of the dump. Remember that anythread could have caused the JVM to run out of memory but it could be a different thread that actually throws the error. For example, thread 1 allocates a byte array that fills up all available heap space, then thread 2 tries to allocate a 1-byte array and throws an error.

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

回答by Antonio Salazar Cardozo

It looks like IBM provides a tool for analyzing those heap dumps: http://www.alphaworks.ibm.com/tech/heaproots; more at http://www-01.ibm.com/support/docview.wss?uid=swg21190476.

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

回答by Fraggle

Once you get a tool to look at the heap dump, look at any thread that was in the Running state in the thread stack. Its probably one of those that got the error. Sometimes the heap dump will tell you what thread had the error right at the top.

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

That should point you in the right direction. Then employ standard debugging techniques (logging, debugger, etc) to hone in on the problem. Use the Runtime class to get the current memory usage and log it as the method in or process in question executes.

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

回答by Arnab Biswas

I generally use Eclipse Memory Analyzer. It displays the suspected culprits (the objects which are occupying most of the heap dump) and different call hierarchies which is generating those objects. Once that mapping is there we can go back to the code and try to understand if there is any possible memory leak any where in the code path.

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

However, OOM doesn't always mean that there is a memory leak. It's always possible that the memory needed by an application during the stable state or under load is not available in the hardware/VM. For example, there could be a 32 bit Java process (max memory used ~ 4GB) where as the VM has just 3 GB. In such a case, initially the application may run fine, but OOM may be encountered as and when the memory requirement approaches 3GB.

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

As mentioned by others, capturing thread dump is not costly, but capturing heap dump is. I have observed that while capturing heap dump application (generally) freezes and only a kill followed by restart helps to recover.

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