《从零开始带你成为JVM实战高手》 笔记八

一、常见导致OOM的原因

1、栈内存溢出,递归、循环调用导致  

举例:找出D盘下文件名中含有txt的文件,D盘下有很多文件夹,每个文件夹下又有很多文件夹,如果使用递归可能导致栈溢出,如何解决? 

解决方案:创建一个有边界的队列,先将根目录下的文件夹全部放入队列。然后创建一个线程消费队列,每取出一个文件夹,就将这个文件夹的子文件夹再入队

2、 堆内存溢出,对象太多导致

3、永久代溢出,类太多导致(通常是反射造成的)

举例:Enhancer不断创建代理对象,导致永久代溢出

解决方案:将Enhancer对象缓存

二、OOM时自动dump快照

添加JVM启动参数

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/usr/local/app/oom

 JDK8的JVM模板

-Xms1024M 
-Xmx1024M 
-Xmn512M 
-Xss1M 
-XX:MetaspaceSize=256M 
-XX:MaxMetaspaceSize=256M
-XX:+UseParNewGC 
-XX:+UseConcMarkSweepGC 
-XX:CMSInitiatingOccupancyFraction=92 
-XX:+UseCMSCompactAtFullCollection 
-XX:CMSFullGCsBeforeCompaction=0 
-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps 
-Xloggc:gc.log 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=./

 

三、第八十六课  案例分析 Tomcat堆内存溢出

1、案例描述

项目使用Tomcat运行时发生OOM异常

2、排查

1)查询日志,发现是堆溢出,并且是Tomcat工作线程报的堆内存溢出

2)使用MAT分析快照,发现是有大量的byte[]数组占据了8G的内存空间

3、原因

1)超时时间设置过长,RPC故障时,请求会卡住,当并发量较大时,对象无法及时被释放,可能导致OOM

2)Tomcat的max-http-header-size属性,tomcat在处理请求时会创建2个数组,每个数组的大小就是上述配置的值。当条件1发生时,更容易导致OOM

4、解决方案

1)修改超时时间,设置为1秒

2)将max-http-header-size设置小一点

四、第八十七课  案例分析 NIO导致堆外内存溢出

1、案例描述

项目使用Jetty服务器运行,发生OOM异常

2、排查

1)查询日志,发现是Direct Buffer memory报内存溢出,即堆外内存溢出 

3、原因

代码中创建DirectByteBuffer对象占用堆外内存,但由于新生代较小,老年代太大。所以young  gc后DirectByteBuffer对象进入老年代,而由于老年代太大,导致一直无法发生full gc,堆外内存无法释放,所以导致了堆外内存溢出

注意:正常情况下NIO已经考虑了这个问题了,所以NIO源码里当发现无法获取堆外内存时,会主动调用System.gc方法,因此正常情况下是不会堆外内存溢出的。但如果在JVM中又设置了

-XX:+DisableExplicitGC

这个参数会导致System.gc失效,因子导致堆外内存溢出

4、解决方案

合理分配内存

 去掉-XX:+DisableExplicitGC这个配置

五、第八十八课  案例分析 RPC导致OOM

1、案例描述

基于Thrift封装了一个RPC框架,平时是用服务A调用服务B,某一日更新服务A的代码并重新部署后,服务B却因为堆内存溢出挂掉了,重启服务B后没多久,服务B又因为堆内存溢出挂掉了

2、排查

1)查询日志,发现发生OOM的位置,是自研的RPC框架

2)使用MAT分析快照,发现占用内存最大的是一个byte[]数组,这个数组就是RPC框架内部的类引用的

3、原因

服务A发送请求时,会把对象序列化给服务B,服务B再反序列化。但服务A对类C添加了字段,而服务B中的类C还是原来的,导致服务B反序列化失败,而自研的RPC框架中,一旦反序列化失败,就会开辟一个4G的byte[]数组,把这个序列化字节流放进去,最终导致OOM

4、解决方案

将4G的byte[]数组改成4M就可以了

六、第九十三课  案例分析 类加载器过多导致服务假死

1、案例描述

上游服务反馈经常一段时间内无法访问服务接口,过一会又可以访问了

2、排查

1)查询日志,并没有发现OOM异常

2)jstat查询gc,怀疑是否由于频繁full gc,stop the world导致服务假死

但通过观察发现,虽然频繁发生gc,但上游服务器并没有反馈服务假死

3)CPU负载过高,导致进程无法获得CPU去执行

但通过top命令观察,发现CPU消费很少,所以不是这个原因

4)内存使用率过高

通过top命令观察,发现JVM消耗超过50%的内存了

3、原因

JVM占用超过50%的内存,而系统本身和其他的进程也要耗费内存。所以有时候JVM的内存虽然没有达到上限,不会发生OOM,但是系统此时内存也占满了,JVM无法申请到内存,于是系统会将这个进程杀掉。但由于这个JVM进程有监控脚本,一旦进程被杀掉,脚本又把进程重新启动起来。所以上游服务会发现有时候调不通,有时候又能调通

4、解决方案

通过MAT找出占用JVM过多内存的原因,发现是创建了过多的类加载器,修改优化代码即可

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值