如何快速制造 OOM

用户头像
Since
关注
发布于: 2020 年 09 月 25 日
如何快速制造OOM

JVM提供了发生OOM时自动保存dump的参数。



本次是想验证一下配置是否正确,以便写到脚本里,所以有了这次的Create OOM。



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是因为没有可用内存进行分配了,所以抛出错误。



Error代表虚拟机无法自我恢复,需要人工介入,重启进程。



Java中的OOM有几下几种常见的类型:

1. java.lang.OutOfMemoryError: Java heap space

堆空间不够了,抛出了OOM异常。



  1. java.lang.OutOfMemoryError: PermGen space/ Metaspace

永久代/元空间溢出了,Java8使用Metaspace取代了PermGen。

Java永久代(元数据)溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。



  1. java.lang.OutOfMemoryError: unable to create new native thread

Linux系统默认一个进程可以创建最多1024个线程,创建的线程数量太多超过了系统的限制,抛出了这个异常。



  1. java.lang.OutOfMemoryError:GC overhead limit exceeded

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



  1. java.lang.OutMemoryError:Direct buffer memory

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



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

  1. java.lang.StackOverflowError

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



  1. 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款产品,分析效果还不错。



以图表形式提供,并且有多个维度的分析,提供了多种情况的聚合报告。





由于堆空间太小,所以对象占用空间不大,线程之类的比较大。



下面是分析结果的地址:

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



发布于: 2020 年 09 月 25 日 阅读数: 24
用户头像

Since

关注

还未添加个人签名 2018.08.14 加入

个人公众号:站在海边看远方 平时写一写工作中遇到的问题,主要开发语言是Java,期待与各位交流

评论

发布
暂无评论
如何快速制造OOM