实战OutOfMemoryError异常

这些异常你是否遇到过?正式开讲之前 , 先罗列一下所知的 OutOfMemoryError (简称 OOM)异常 , 看看这些异常工作中你是否也遇到过?
Java 堆内存溢出:java.lang.OutOfMemoryError: Java heap space
垃圾回收内存溢出:java.lang.OutOfMemoryError: GC overhead limit exceeded
方法区溢出:java.lang.OutOfMemoryError: PermGen space
Metaspace 内存溢出:java.lang.OutOfMemoryError: Metaspace
直接内存内存溢出:java.lang.OutOfMemoryError: Direct buffer memory
栈内存溢出:java.lang.StackOverflowError
创建本地线程内存溢出:java.lang.OutOfMemoryError: Unable to create new native thread
数组超限内存溢出:java.lang.OutOfMemoryError:Requested array size exceeds VM limit
在实际工作中 , 若真遇到了上面罗列的这些内存溢出的异常 , 你是否能够根据异常提示迅速定位是哪儿出了幺蛾子 , 并是否能够铲除这些幺蛾子呢?
希望通过此篇分享 , 尽量能够让大家了解每个异常发生的场景 , 并能够掌握每个异常场景的应对之策 。
实战OutOfMemoryError异常文章插图
如上图示意 , 按照内存共享来划分 JVM 内存 , 主要划分为线程共享内存区域(堆、方法区)、线程私有内存区域(程序计数器、虚拟机栈、本地方法栈)、直接内存 。 而在《Java 虚拟机规范》的规定里 , 除了程序计数器外 , 虚拟机内存的其它几个运行时区域都可能发生 OOM 异常 , 接下来通过代码来剖析一下各种 OutOfMemoryError(OOM)的场景 。
实战:OutOfMemoryError 异常场景一
java.lang.OutOfMemoryError: Java heap space
/** * VM options:-Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError * @author 一猿小讲 */public class HeapOOM {public static void main(String[] args) {byte[] bytes = new byte[20 * 1024 * 1024];System.out.println(bytes);}}理论且不谈 , 直接抛代码 , 代码很简单 , 创建一个字节数组对象 , 要分配 20M 的空间 。 若在运行程序时指定 VM 参数:

  • 通过参数 -Xms10m -Xmx10m 将堆的最小值与最大值都设置为 10M , 即限制 Java 堆的大小为 10MB , 并且避免堆自动扩展;
  • 通过参数 -XX:+HeapDumpOnOutOf-MemoryError 让虚拟机在出现内存溢出异常的时候 Dump 出当前的内存堆转储快照以便进行事后分析 。
指定 VM options 后的运行结果:
java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid35115.hprof ...Heap dump file created [1033561 bytes in 0.005 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat HeapOOM.main(HeapOOM.java:7)为什么呢?简单解释原因 , -Xms10m -Xmx10m 限制了堆的最大值为 10M , 而 new byte[20 * 1024 * 1024] 需要 20M 的空间 , 则堆内存明显不够 , 则直接导致 OOM 。
面对此种异常 , 常规解决思路:
  • 要检查一下代码是否存在优化的空间;
  • 依据内存溢出时的快照文件 xx.hprof 来判断是否存在内存泄露 , 不需要的对象有没有被回收掉;
  • 调节虚拟机的堆参数(-Xms -Xmx) , 适当调大堆内存 。
场景二
java.lang.OutOfMemoryError: GC overhead limit exceeded
/** * VM options:-Xmx6m -XX:+HeapDumpOnOutOfMemoryError * @author 一猿小讲 */public class HeapOOM {static class GirlFriend {}public static void main(String[] args) {List list = new ArrayList();while (true) {list.add(new GirlFriend());}}}理论且不谈 , 直接抛代码 , 代码很简单 , 一直往集合中加入新创建的对象(虚妄的单身狗生活:一直创建女朋友对象 。 )
若在运行程序时指定 VM 参数:
  • 通过参数 -Xmx6m 将堆的最大值设置为 6M;
  • 通过参数 -XX:+HeapDumpOnOutOf-MemoryError 让虚拟机在出现内存溢出异常的时候 Dump 出当前的内存堆转储快照以便进行事后分析 。
指定 VM options 后的运行结果:
java.lang.OutOfMemoryError: GC overhead limit exceededDumping heap to java_pid35304.hprof ...Heap dump file created [12557270 bytes in 0.082 secs]Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceededat HeapOOM.main(HeapOOM.java:16)为什么呢?来段洋文 , 尝试解读一下 。
The parallel(concurrent) collector will throw an OutOfMemoryError if too much time is being spent in garbage collection: if more than 98% of the total time is spent in garbage collection and less than 2% of the heap is recovered, an OutOfMemoryError will be thrown.
大概意思应用程序在垃圾收集上花费了太多时间 , 但是却没有什么卵用 , 默认超过 98% 的时间用来做GC却回收了不到2%的内存时将会抛出 OutOfMemoryError 异常 。
面对此种异常 , 常规解决思路: