如何快速制造OOM
为了验证JVM发生OOM时自动保存dump的参数配置是否正确,需要验证一下,所以有了这次的验证过程。
OOM
OOM是OutOfMemory的缩写,内存溢出,Java中的OOM是在java.lang.OutOfMemoryError
类中定义的,类注释如下:
Thrown when the Java Virtual Machine cannot allocate an obje because it is out of memory, and no more memory could be made
available by the garbage collector.
发生OOM是因为没有可用内存进行分配了,所以抛出错误,错误代表虚拟机无法自我恢复,需要外力介入。
Java中的OOM有几下几种常见的类型:
-
java.lang.OutOfMemoryError: Java heap space
堆空间不够了,抛出了OOM异常。 -
java.lang.OutOfMemoryError: PermGen space/ Metaspace
永久代/元空间溢出了,Java8使用Metaspace取代了PermGen。
Java永久代(元数据)溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。 -
java.lang.OutOfMemoryError: unable to create new native thread
Linux系统默认一个进程可以创建最多1024个线程,创建的线程数量太多超过了系统的限制,抛出了这个异常。 -
java.lang.OutOfMemoryError:GC overhead limit exceeded
在并行或者并发回收器在GC回收时间过长、超过98%的时间用来做GC并且回收了不到2%的堆内存,然后抛出这种异常进行提前预警,用来避免内存过小造成应用不能正常工作 -
java.lang.OutMemoryError:Direct buffer memory
写NIO程序使用ByteBuffer来读取或者写入数据,这是基于通道(channel)和缓冲区(buffer)的IO方式,可以使用Native函数库直接分配堆内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作,这样在一些场景中能够提高性能,避免了Java堆和native堆中来回复制数据。
还有2种和OOM有关系的异常:
-
java.lang.StackOverflowError
是JVM的线程由于递归或者方法调用层次太多,占满了线程堆栈而导致的,线程堆栈默认大小为1M。 -
java.net.SocketException: Too many open files
是由于系统对文件句柄的使用是有限制的,而某个应用程序使用的文件句柄超过了这个限制,就会导致这个问题
模拟过程
介绍了一下常见的OOM,我们采用最简单的Java heap space进行模拟,将堆内存设置的小一点,然后循环创建对象。
JVM参数设置:
-Xms8m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath="E:\heapdump.hprof"
然后使用while循环创建对象
public class HeapSpaceOomTest {
public static void main(String[] args) {
ArrayList<Object> list = new ArrayList<>(100000);
while (true) {
list.add(new Object());
}
}
}
运行结果如下,可以看到按照预期抛出了OOM异常,然后生成了dump文件
java.lang.OutOfMemoryError: Java heap space
Dumping heap to E:\heapdump.hprof ...
Heap dump file created [9849369 bytes in 0.030 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at com.fc.oom.HeapSpaceOomTest.main(HeapSpaceOomTest.java:15)
Process finished with exit code 1
将生成的dump文件使用工具进行分析,这里推荐一个在线产品,
https://console.perfma.com/
这是前阿里的JVM大佬寒泉子创办的公司,他们提供了免费的社区产品,有虚拟机参数分析工具、线程dump分析工具、堆dump分析3款产品,分析效果还不错,以图表形式提供,并且有多个维度的分析报告
image.png由于堆空间太小,所以对象占用空间不大,线程之类的比较大。
下面是分析结果的地址:
https://memory.share.perfma.com/detail/1900154
小结
JDK1.7常常会发生永久代的OOM,1.8使用元空间替换永久代之后这种情况就少了不少,了解常见的OOM,以及如何保存OOM dump对分析故障会有一些帮助。
参考文章
https://www.jianshu.com/p/4645254be259
https://www.jianshu.com/p/0744abda44cb
https://www.cnblogs.com/shemlo/p/11665917.html