Java.Lang.OutOfMemoryError异常总结

424 阅读6分钟

没有什么错误比java.lang.OutOfMemory更让人头疼的了。当这种情况发生时,您的应用程序将运行到一个非常不可预知的状态,经常挂断并且不处理新的请求,在用户浏览器中抛出难看的堆栈跟踪等。最流行的(短期内有效的)修复OutOfMemory错误的方法就是重新启动应用程序或加大jvm相关配置项。

虽然java堆耗尽是最常见的OutOfMemory错误,但确实有其他几种类型的OutOfMemory可能发生。在本文中,我将向您展示这些不同类型的OutOfMemory错误及其含义。

Java.Lang.OutOfMemoryError: Java Heap Space

最受欢迎的。这是JVM崩溃的时候,无法在堆中分配内存。您的应用程序可能会出现挂起,对用户请求的响应非常慢。

Java heap space Heap size,是指java程序运行过程中JVM可以调配使用的内存空间的设置。JVM在启动的时候会自动设置Heap size的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)是物理内存的1/4。可以利用JVM提供的-Xmn -Xms -Xmx等选项可进行设置。Heap size 的大小是Young Generation 和Tenured Generaion 之和。提示:在JVM中如果98%的时间是用于GC且可用的Heap size 不足2%的时候将抛出此异常信息。提示:Heap Size 最大不要超过可用物理内存的80%,一般的要将-Xms和-Xmx选项设置为相同,而-Xmn为1/4的-Xmx值。

什么原因导致的堆空间溢出错误?

常见的原因是内存泄漏(假设,这是一个很大的假设,您已经通过-Xmx分配了足够的最大堆)。应用程序正在使用内存,但从未释放它们(即垃圾收集无法回收内存)。

另一个原因java.lang.OutOfMemory错误:Java堆空间是一种错误的代码,它会突然开始消耗大量内存—例如,代码进入无限循环,或者试图在不分页的情况下将数百万条记录加载到内存中。

据我所知,一个不太常见的原因是过度使用“终结器”finalizers。当类有finalize方法时,该类型的对象不会通过普通GC被回收。它们进入一个特殊的终结队列,稍后处理。

堆空间溢出你能做些什么?

涉及配置项:Xms Xmx

你需要弄清错误的根源。说起来容易做起来难。如果您使用的是第三方库和API,那么API库中也有可能发生内存泄漏。我的方法是采用堆转储heap dump并使用Eclipse内存分析器MAT进行分析。您也可以尝试jcmd测试GC.class_histogram类直方图

Java.Lang.OutOfMemoryError: Metaspace 、 PermGen

注意:元空间是从Java8引入的。在java8之前的世界里,应该是PermGen 。

Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中,,它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对 PermGen space进行清理,

什么原因导致的元空间溢出?

通常是由于使用本机代码的第三方库。请注意,耗尽元空间并不表示应用程序中存在java内存泄漏(即,这很可能不是代码问题)。您应该关注应用程序使用的各种库。

还有你的应用中有很多CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar,其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。

元空间溢出你能做些什么?

涉及配置项:MaxMetaSpaceSize MaxPermSize

增加MaxMetaSpaceSize作为第一道防线。您可以通过添加java命令行参数-XX:MaxMetaspaceSize=<size> 或者 -XX:MaxPermSize来实现这一点。如果您必须增加超过1GB(对于大多数应用程序),我将开始推测源于第三方库的本机内存问题。

Java.Lang.OutOfMemoryError: Request Size Bytes For Reason. Out Of Swap Space ?

这绝对是本机内存问题。当本机内存中的分配请求失败时,JVM抛出这个命令。您可以通过确保没有用完Java堆(通过检查详细的GC日志或监视堆使用情况)来再次检查这一点。

什么原因导致的交换空间不足?

本机内存泄漏是主要嫌疑。这种情况可能有很多原因。例如,如果应用程序错误地打开了许多网络套接字,或者第三方库错误地打开了大量文件。

交换空间不足你能做些什么?

对于初学者,请查看JVM生成的致命错误日志文件。此文件通常命名为hs_err_pid.log文件. 它位于JVM进程的工作目录中(或通过-XX:ErrorFile=<file>)。此文件通常包含有关本机调用的信息。您还可以使用操作系统工具进行调试(如gdb、dtrace、strace、lstack、pstack等)。老实说,处理本地内存问题一点都不好玩。但只要坚持不懈,你就能找到罪魁祸首。

Java.Lang.OutOfMemoryError: GC Overhead Limit Exceeded

这是一个有趣的问题。如消息所示,GC正在产生相当大的开销。这意味着应用程序将大部分时间花在GC中,而不是做应用程序工作。用户通常会看到挂起的浏览器会话和糟糕的响应时间。

什么原因导致超出GC开销限制?

最可能的原因是堆被活动对象填满,新的内存分配请求速率太高,JVM无法处理。根据Oracle的文档,如果Java进程花在垃圾收集上的时间超过了它的大约98%,如果它恢复的堆不到2%,并且一直在进行最后5个(编译时间常数)连续的垃圾收集,那么java.lang.OutOfMemoryError错误被抛出。

超出GC开销限制你能做些什么?

尝试增加堆大小(-Xmx)。如果这不能解决问题,那么您可能必须通过进行堆转储并查看堆中的对象来找到根本原因。请注意,可以使用-XX:-usegoverheadlimit java命令行参数关闭此异常。

Java.Lang.OutOfMemoryError: Requested Array Size Exceeds VM Limit

这也是一个有趣的问题。当JVM无法为数组(或集合对象)分配内存空间时,就会发生这种情况

什么原因导致请求的数组大小超过VM限制?

可能是应用程序试图错误地创建一个巨大数组的代码问题。另一种可能是堆大小错误地太小(-Xmx

请求的数组大小超过VM限制错误你能做些什么?

检查最大堆大小以确保分配了足够的内存。检查以确保您的应用程序没有试图创建一个巨大的数组。

您可以使用详细的GC日志和堆转储来查找根本原因。本机内存泄漏可能很难诊断,您可能需要调用操作系统工具来进一步排除故障。