如何快速制造 OOM
JVM提供了发生OOM时自动保存dump的参数。
本次是想验证一下配置是否正确,以便写到脚本里,所以有了这次的Create OOM。
OOM
OOM是OutOfMemory的缩写,内存溢出。
Java中的OOM是在java.lang.OutOfMemoryError
类中定义的,类注释如下:
发生OOM是因为没有可用内存进行分配了,所以抛出错误。
Error代表虚拟机无法自我恢复,需要人工介入,重启进程。
Java中的OOM有几下几种常见的类型:
1. 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参数设置:
然后使用while循环创建对象
运行结果如下,可以看到按照预期抛出了OOM异常,然后生成了dump文件
将生成的dump文件使用工具进行分析,这里推荐一个在线产品,
这是前阿里的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
版权声明: 本文为 InfoQ 作者【Since】的原创文章。
原文链接:【http://xie.infoq.cn/article/8422df4bed559bb06e6321dd6】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论