记一次JAVA FULL GC问题处理【1】

1: 线上出现连续几次服务不可用, 

 

2: 刚开始通过查询内存JAVA对象大小的数量看是不是有内存泄露

jmap -histo 8 发现内存中,确实存在一些不应该存在的对象。
如始红圈的点,这个是POI 工具中引用的XSSFWorkbook因而本次发生Full GC原因,归结为使用POI工具不合适。
 

3: jmap -dump:format=b,file=heap.binnew 8 通过jvisualvm 查看这些对象的信息 

 

 发现存在众多的无根引用对象,也就是内存泄露的问题,基本可以排除。

看到这里,觉得找到了真像,确认为是由于使用POI 的非流式服务,导致了在一次请求导出流程中,生成了过多的对象,导致YGC无法回收。

如果导出时间过长,从而导致这些对象进入老年代。  (看到这里,当时心里也有疑问,为什么不直接发生OOM呢)。 不过在现网找,确实也发地生过OOM。

 

4:  优化方式: 后来使用流式的SXSSFWorkbook 做导出操作。

并在测试环境做了对比测试, 因为测试环境只有400M,所以没法和现网比较(好吧,这个我承认我们很挫,尽然没有性能测试环境) 。只很通过修改前更的对比测试了。

在原来修改前使用XSSFWorkbook时,必然每次都发生,FULL GC 也有可能发生OOM。 

在修改成SXSSFWorkbook后,情况大大的好转,发生FULL GC概率减少。 当时考虑到现网的内存比测试环境好,而且测试环境使用的到出极限数量比现网还大,觉得上线没有问题。

 

5:不好意思,运行十来天后,现网再次发生连续的FULL GC , 当时觉得不科学呀。通过监控看到YGC 基本失效了,FULL GC到时连续不断

发现FULL GC时间几秒到几十秒都会发生一次

 

4: 导出内存信息 
jmap -dump:format=b,file=heap.binnew 8 通过jvisualvm 查看这些对象的信息 
发现很多对象的引用都是空的, 结合JAVA回收机制,可知是应该在YGC回收的对象,跑到了老年代去了。
 
6: 因为POI的问题已经解决,而导出的堆对象也不再是POI,得想到是其他问题了。考虑到YGC失效原因,只有新生代对象太大,页Survivor放不下,才会去老年贷,发现Survivor的大小只有42M的,4M确实过小
 

 7: 也就是申请到的Survior的空间过小,考虑到Survior 空间过小的原因,先从启动参数说起。

JAVA启动参数: 

-Xmx4g -Xms4g -Xmn1600m -XX:+UseParallelGC   可以看出,配置极少。 也就是除了指定新生代大小后,根本本没有指定 Survior区大小。但Survivor有个默认比例,完全与默认的的单个Survior应该有 有整个年轻代的1/10 。 

java -XX:+PrintFlagsFinal -version |grep HeapSize
java -XX:+PrintFlagsFinal -version |grep NewRatio

/usr/local/tomcat # java -XX:+PrintFlagsFinal -version |grep NewRatio
uintx NewRatio = 2 {product}

0

 

系统计算的可用堆内存, 是3.44G,与申请的内存大小4G有差距。

 

8: 后改用,并降低本机器其它的机器申请的Xmx数量,让本服务获得更多的内存,解决本问题。并取消JVM的自适应,防止内存不够时,把Survivor的内存放得更小

-Xmx4g -Xms4g -Xmn16

00m -XX:+UseParallelGC -XX:-UseAdaptiveSizePolicy -XX:SurvivorRatio=8 -Djdk.tls.ephemeralDHKeySize=2048

9: 后续声音,我们的现网缩容了,原来20G的现网, 现在只有16G了, 而且docker服务多跑了一个。 

 

 10: 那本次Full GC Survior区值小的原因是什么呢?

 

 

posted @ 2021-05-17 11:21  风云快客  阅读(396)  评论(0编辑  收藏  举报