常⻅ OOM 异常分析

---------作者: 捡⽥螺的⼩男孩

常⻅ OOM 异常分析

  • 堆溢出
  • 栈溢出
  • 方法区溢出
  • 本机直接内存溢出
  • GC overhead limit exceeded

一、堆溢出

Java 堆⽤于存储对象实例,只要不断地创建对象,并且保证 GC Roots 到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么
在对象数量到达最⼤堆的容量限制后就会产⽣内存溢出异常。

原因

  • 无法在java堆中分配对象
  • 应用程序保存了无法被GC回收的对象
  • 应用程序过度使用finalizer

排查解决思路

  1. 查找关键报错信息,如java.lang.OutOfMemoryError: Java heap space。

  2. 使⽤内存映像分析⼯具(如 Eclipsc Memory Analyzer 或者 Jprofiler)对 Dump 出来的堆储存快照进⾏分析,分析清楚是内存泄漏还是内存溢出。

  3. 如果是内存泄漏,可进⼀步通过⼯具查看泄漏对象到 GC Roots 的引⽤链,修复应⽤程序中的内存泄漏。

  4. 如果不存在泄漏,先检查代码是否有死循环,递归等,再考虑⽤ -Xmx 增加堆⼤⼩。


二、栈溢出

关于虚拟机栈和本地⽅法栈,在 Java 虚拟机规范中描述了两种异常:

  • 如果线程请求的栈深度⼤于虚拟机所允许的深度,将抛出 StackOverflowError 异常;

  • 如果虚拟机栈可以动态扩展,当扩展时⽆法申请到⾜够的内存时会抛出 OutOfMemoryError 异常。

原因

  • 在单个线程下,栈帧太⼤,或者虚拟机栈容量太⼩,当内存⽆法分配的时候,虚拟机抛出StackOverflowError 异常。

  • 不断地建⽴线程的⽅式会导致内存溢出。

排查解决思路

  1. 查找关键报错信息,确定是 StackOverflowError 还是 OutOfMemoryError;
  2. 如果是 StackOverflowError,检查代码是否递归调⽤⽅法等;
  3. 如果是 OutOfMemoryError,检查是否有死循环创建线程等,通过 - Xss 降低的每个线程栈⼤⼩的容量。

三、⽅法区溢出

⽅法区,(⼜叫永久代,JDK8 后,元空间替换了永久代),⽤于存放 Class 的相关信息,如类名、访问修饰符、常量池、字段描述、⽅法描述等。运⾏时产⽣⼤量的类,会填满⽅法区,造成溢出。

原因

  • 使⽤ CGLib ⽣成了⼤量的代理类,导致⽅法区被撑爆。

  • 在 Java7 之前,频繁的错误使⽤ String.intern ⽅法。

  • ⼤量 jsp 和动态产⽣ jsp。

  • 应⽤⻓时间运⾏,没有重启。

排查解决思路

  1. 检查是否永久代空间设置得过⼩;
  2. 检查代码是否频繁错误得使⽤ String.intern ⽅法;
  3. 检查是否跟 jsp 有关;
  4. 检查是否使⽤ CGLib ⽣成了⼤量的代理类;
  5. 重启⼤法,重启 JVM。

四、本机直接内存溢出

直接内存并不是虚拟机运⾏时数据区的⼀部分,也不是 Java 虚拟机规范中定义的内存区域。但是,这部分内存也被频繁地使⽤,⽽且
也可能导致 OOM。

在 JDK1.4 中新加⼊了 NIO(New Input/Output) 类,它可以使⽤ native 函数库直接分配堆外内存,然后通过⼀个存储在 Java 堆中的
DirectByteBuffer 对象作为这块内存的引⽤进⾏操作。这样能在⼀些场景中显著提⾼性能,因为避免了在 Java 堆和 Native 堆中来回复
制数据。

原因

  • 本机直接内存的分配虽然不会受到 Java 堆⼤⼩的限制,但是受到本机总内存⼤⼩限制。

  • 直接内存由 -XX:MaxDirectMemorySize 指定,如果不指定,则默认与 Java 堆最⼤值(-Xmx 指定)⼀样。

  • NIO 程序中,使⽤ ByteBuffer.allocteDirect(capability) 分配的是直接内存,可能导致直接内存溢出。

排查解决思路

  1. 检查代码是否恰当。
  2. 检查 JVM 参数 - Xmx,-XX:MaxDirectMemorySize 是否合理。

五、GC overhead limit exceeded

  • 这个是 JDK6 新加的错误类型,⼀般都是堆太⼩导致的。

  • Sun 官⽅对此的定义:超过 98% 的时间⽤来做 GC 并且回收了不到 2% 的堆内存时会抛出此异常。

解决方案

  1. 检查项⽬中是否有⼤量的死循环或有使⽤⼤内存的代码,优化代码。
  2. 检查 JVM 参数 - Xmx -Xms 是否合理。
  3. dump 内存,检查是否存在内存泄露,如果没有,加⼤内存。

六、总结

⼏种常⻅ OOM 异常:

  • java.lang.OutOfMemoryError: Java heap space
  • java.lang.OutOfMemoryError: unable to create new native thread
  • java.lang.OutOfMemoryError: Metaspace
  • java.lang.OutOfMemoryError: Direct buffer memory
  • java.lang.OutOfMemoryError: GC overhead limit exceeded

posted @ 2020-02-20 23:13  Eric小星  阅读(266)  评论(0编辑  收藏  举报