如何快速制造OOM

2020-09-24  本文已影响0人  站在海边看远方

为了验证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有几下几种常见的类型:

  1. java.lang.OutOfMemoryError: Java heap space
    堆空间不够了,抛出了OOM异常。

  2. java.lang.OutOfMemoryError: PermGen space/ Metaspace
    永久代/元空间溢出了,Java8使用Metaspace取代了PermGen。
    Java永久代(元数据)溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。

  3. java.lang.OutOfMemoryError: unable to create new native thread
    Linux系统默认一个进程可以创建最多1024个线程,创建的线程数量太多超过了系统的限制,抛出了这个异常。

  4. java.lang.OutOfMemoryError:GC overhead limit exceeded
    在并行或者并发回收器在GC回收时间过长、超过98%的时间用来做GC并且回收了不到2%的堆内存,然后抛出这种异常进行提前预警,用来避免内存过小造成应用不能正常工作

  5. java.lang.OutMemoryError:Direct buffer memory

写NIO程序使用ByteBuffer来读取或者写入数据,这是基于通道(channel)和缓冲区(buffer)的IO方式,可以使用Native函数库直接分配堆内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作,这样在一些场景中能够提高性能,避免了Java堆和native堆中来回复制数据。

还有2种和OOM有关系的异常:

  1. java.lang.StackOverflowError
    是JVM的线程由于递归或者方法调用层次太多,占满了线程堆栈而导致的,线程堆栈默认大小为1M。

  2. 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

上一篇下一篇

猜你喜欢

热点阅读