您好,这里是「码农镖局」CSDN博客,欢迎您来,欢迎您再来~
前面说了一般应用的OOM情况,但是OOM不知发生在应用层,有时候专门负责运行Java的Tomcat也会偶尔罢工一下,抛出OOM异常。因为Tomcat本身也是一个JVM进程。
为了复现Tomcat的OOM,可以给JVM加上下面的参数:
-XX:+HeapDumpOnOutOfMemoryError
当内存发生OOM时导出一份快照到指定的位置,默认导出的快照文件存放在应用的根目录。
通过追溯问题可以定位问题:
1、Tomcat线程会创建大量的byte[]数组;
2、每秒请求才100,但每个请求都需要4秒的时间;
3、Tomcat配置文件出现了max-http-header-size:10000000;
4、这个配置导致Tomcat工作线程在处理请求时会创建2个数组;
5、400 × 2 × 10000000 = 8G,内存OOM;
6、之所以处理时间长达4秒是因为RPC超时,超时时间刚好设置4秒;
7、问题根本原因是下游故障导致请求失败,整个系统卡住直到超时结束。
所以相应的优化方案也很简单:
1、先解决上下游的系统故障;
2、将超时时间调整为1秒;
3、将Tomcat参数max-http-header-size适当调小。
除了Tomcat,在一些非常轻量级的应用中,Jetty也会被部署到生产环境中。这是使用Jetty堆外内存发生的一次OOM。Direct buffer memory,就是堆外内存,是JVM堆内存之外的一块内存空间,不属于JVM管理范畴。
Jetty使用DirectByteBuffer类申请堆外内存空间,DirectByteBuffer对象存储在JVM堆内存中。因此当DirectByteBuffer对象没人引用了成了垃圾对象之后,会在Young GC或者Full GC时被回收掉。
这种情况经常发生在:
1、当系统承载超高并发,瞬时请求很大时;
2、当创建了很多DirectByteBuffer对象且还没被回收时继续创建更多DirectByteBuffer对象。
那么在本案例中导致OOM的原因是:
1、通过jstat分析JVM运行情况发现,系统并发量并不高,但每个请求处理较为耗时;
2、系统上线时内存分配不合理,年轻代与老年代的比例为1:4,导致S0/S1极小;
3、Java NIO会主动调用System.gc()完成JVM的垃圾回收;
4、JVM参数中增加-XX:+DisableExplicitGC,这个参数使得显式调用垃圾回收不生效;
5、由此导致堆外OOM的故障。
解决方案是:
1、合理分配内存,给年轻代更多内存;
2、放开-XX:+DisableExplicitGC,让System.gc()生效。
感谢您的大驾光临!欢迎骚扰,不胜荣幸~