1.问题背景
接收到监控系统发出的提醒,提示线上环境一服务的老年代使用情况超过阈值,高达95%。这里针对问题的分析和处理,做一个简单的回顾和总结。
jdk环境:jdk8
2.排查过程
2.1处理过程
(1)移除当前节点的流量,避免对线上的请求造成影响(线上环境中首先保证恢复集群可用,然后在定位问题)
(2)生成当前机器的dump文件(jmap -dump:format=b,file=heapdump.hprof pid)
(3)当前机器的gc日志文件
2.2推测原因
(1)存在不可回收的大对象,导致老年代的内存空间被占用
(2)新生代内存大小设置不合理,导致大部分对象直接存放老年代
2.3问题定位
当前jvm配置参数:
-Xms4G -Xmx4G -Xmn1G -Xss512K -XX:SurvivorRatio=8 -XX:+UseCMSCompactAtFullCollection -XX:MaxTenuringThreshold=10 -XX:CMSInitiatingOccupancyFraction=80 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/logs -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/opt/logs/gc.txt
2.3.1 分析dump文件
使用idea分析当前的dump文件,发现当前系统中的对象没有过大文件,因为当前集群的主要职能就是接收消息,用来做任务的计算以及状态的更新,因此会产生大量的临时对象。
2.3.2 分析gc日志
查看最后一次gc日志,发现问题,from区和to区仅为1536K,与jvm配置的值存在很大的偏差。
按照配置文件计算from和to区的大小应该在100M左右,目前仅为1M左右。
(1)查看当前集群使用的垃圾回收器和内存配置
发现集群使用的使用Parallel GC垃圾回收器,且Eden区的内存大小远超过配置的大小,怀疑Parallel GC垃圾回收器是否存在特殊的配置。最后定位到AdaptiveSizePolicy会根据每次gc动态的调整Eden、Survivor 区的大小;
(2)查看当前集群是否开启AdaptiveSizePolicy
发现集群中确实开启了AdaptiveSizePolicy的配置
(3)关闭AdaptiveSizePolicy配置
Eden、Survivor 区的大小正常。问题解决
3.结论
- 应用使用 JDK 1.8,其默认回收器是 Parallel Scavenge,并且默认开启了 AdaptiveSizePolicy。
- AdaptiveSizePolicy 动态调整 Eden、Survivor 区的大小,存在将 Survivor 区调小的可能。当 Survivor 区被调小后,部分 YGC 后存活的对象直接进入老年代。老年代占用量逐渐上升从而触发 FGC,导致较长时间的 STW。
- 建议使用 CMS 垃圾回收器,默认关闭 AdaptiveSizePolicy。(-XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=80)
- 建议在 JVM 参数中加上 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/logs -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/opt/logs/gc.txt,让 GC log 更加详细,方便定位问题。