没有什么错误比java.lang.OutOfMemory更让人头疼的了。当这种情况发生时,您的应用程序将运行到一个非常不可预知的状态,经常挂断并且不处理新的请求,在用户浏览器中抛出难看的堆栈跟踪等。最流行的(短期内有效的)修复OutOfMemory错误的方法就是重新启动应用程序或应用程序服务器。
虽然java堆耗尽是最常见的OutOfMemory错误,但确实有其他几种类型的OutOfMemory可能发生。在本文中,我将向您展示这些不同类型的OutOfMemory错误及其含义。
java.lang.OutOfMemoryError: Java heap space
最受欢迎的。这是JVM崩溃的时候,无法在堆中分配内存。您的应用程序可能会出现挂起,对用户请求的响应非常慢。
例如,假设Java应用程序的最大内存为2GB(通过-Xmx
标志)。当整个2GB用完并且GC无法回收任何内存时,下一个对象的内存分配请求将失败java.lang.OutOfMemory错误。
什么原因导致的堆空间溢出错误?
常见的原因是内存泄漏(假设,这是一个很大的假设,您已经通过-Xmx
分配了足够的最大堆)。应用程序正在使用内存,但从未释放它们(即垃圾收集无法回收内存)。如果要绘制一个堆利用率图,您将看到一个向上缓慢的阶梯(作为一个健康的应用程序,应该显示一个“锯齿”模式)。当您循环使用应用程序服务器时,您将获得一个新的内存配额,循环将再次开始。如果内存泄漏,根据泄漏的严重程度,应用程序可能会在每次重新启动后持续几天(或几个小时)。
另一个原因java.lang.OutOfMemory错误:Java堆空间是一种错误的代码,它会突然开始消耗大量内存—例如,代码进入无限循环,或者试图在不分页的情况下将数百万条记录加载到内存中。
据我所知,一个不太常见的原因是过度使用“终结器”finalizers。当类有finalize
方法时,该类型的对象不会通过普通GC被回收。它们进入一个特殊的终结队列,稍后处理。
堆空间溢出你能做些什么?
你需要弄清错误的根源。说起来容易做起来难。如果您使用的是第三方库和API,那么API库中也有可能发生内存泄漏。我的方法是采用堆转储heap dump并使用Eclipse内存分析器MAT进行分析。您也可以尝试jcmd测试GC.class_histogram
类直方图
java.lang.OutOfMemoryError: Metaspace
注意:元空间是从Java8引入的。在java8之前的世界里,应该是PermGen。
Metaspace是JVM存储“类元数据”和“内部字符串”的地方。对于一个使用大量第三方库的应用程序,我看到这个空间的利用率提高了。空间由MaxMetaSpaceSize
命令行参数限制。
什么原因导致的元空间溢出?
通常是由于使用本机代码的第三方库。请注意,耗尽元空间并不表示应用程序中存在java内存泄漏(即,这很可能不是代码问题)。您应该关注应用程序使用的各种库。
元空间溢出你能做些什么?
增加MaxMetaSpaceSize作为第一道防线。您可以通过添加java命令行参数-XX:MaxMetaspaceSize=<size>
来实现这一点。如果您必须增加超过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日志和堆转储来查找根本原因。本机内存泄漏可能很难诊断,您可能需要调用操作系统工具来进一步排除故障。
另外还有一些很少发生的内存溢出错误类型:
- java.lang.OutOfMemoryError: Compressed class space
- java.lang.OutOfMemoryError: reason stack_trace_with_native_method
- java.lang.OutOfMemoryError: nativeGetNewTLA
除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/1322.html
暂无评论