程序启动时发生了两次full GC?
原因
jvm参数配置永久代时使用了-XX:MaxPermSize=250M -XX:PermSize=250M
这两个参数,在jdk8之后,这俩参数已失效,永久代改为元空间;
解决
调整参数为-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m
;若不配置,默认大小是20M,建议指定其大小避免full gc
业务程序的FullGc问题
发现问题
- 接口响应偶尔超时
- GC日志文件过大
定位
-
top命令,查看系统负载、cpu、内存等(无异常)
-
jstat命令,查看GC使用情况
- 发现FullGC次数,平均每天200+
-
gceasy.io分析GC日志
- 某些时段yongGC之后的大小仍有500M+的大小
- 平均stw时间 900ms、最大5000ms、吞吐量:98.204%
解决
-
以上原因得出触发fullgc是因为堆内存过小、suvisor太小导致过早提升
-
增加堆内存大小由
Xmx=2G Xmn=1G
改为Xmx=6G Xmn=5G
- Gc次数20/h
- gceasy.io分析GC,平均stw时间 87ms、最大340ms、吞吐量:98.204%
有所改善,但gc仍很频繁
-
观察GC日志发现,gc时间很规律都是在半点或者整点是触发,查看代码发现是最近新上的定时任务做数据汇总,一每次次性加载数据太多,导致一段时间内大量的对象创建,YGC垃圾回收的速率不及时,致使大量对象直接进入老年代,根据这种情况对定时任务进行优化,分批汇总后,效果明显
-
gc暂停时间如下图
-
j连续运行一段时间后,没有再发生过full gc情况
总结
-
定位服务器资源和GC情况,确定问题
- 内存问题,频繁YGC
- 过早提升,对象直接进入老年代
- 提升失败,碎片过多\老年代内存不足,导致FullGc
- 提升失败或者新产生的对象需要晋升放入老年代,cms回收速率小于对象进入老年代的速率,触发concurrent mode failer,cms退化为Serail单线程收集并STW
-
相关工具监控分析
- 分析服务器资源使用情况:prometheus\grafna\node_exporter\process_exporter
- jvm使用情况:jvisualvm\jstat\arthas
- gc日志分析:gceasy.io