• 常⻅ 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

  • 相关阅读:
    JAVA语法之小结
    JAVA之经典Student问题1
    Android之动画1
    Android之屏幕测试
    Android之点击切换图片
    Android之标签选项卡
    Android简单计算器
    Javascript之相册拖动管理
    Javascript之改变盒子颜色
    CSS之照片翻转
  • 原文地址:https://www.cnblogs.com/aric2016/p/12339697.html
Copyright © 2020-2023  润新知