随着项目数据持续不断增长,在触发老年代GC内存回收时,暂停查询或搜索致使业务请求等待过长导致业务订单超时。
问题分析
内存分区简单介绍:
新生代Eden区(存放新创建的对象)满时,就会触发Minor GC(也称Young GC)
新生代Survivor区(存放已经经过一次垃圾回收的对象)
老年代old区(存放已经存活了一段时间的对象的区域)满时,则会触发Full GC(也称Old GC)
通过不断对测试环境压力测试,模拟生产环境遇到的报错信息。
# 测试环境日志
[2023-04-29T17:40:55,304][WARN ][o.e.m.j.JvmGcMonitorService] [node-3] [gc][young][408][152] duration [1.2s], collections [1]/[2.5s], total [1.2s]/[4.5s], memory [434.1mb]->[352.4mb]/[990.7mb], all_pools {[young] [160.1mb]->[41.8mb]/[266.2mb]}{[survivor] [16.5mb]->[33.2mb]/[33.2mb]}{[old] [257.4mb]->[277.3mb]/[691.2mb]}
分析日志:
-
时间戳:2023-04-29T17:40:55,304
-
日志级别:WARN
-
类名:o.e.m.j.JvmGcMonitorService
-
节点名称:node-3
-
GC类型:young
-
GC耗时:1.2s
-
GC时间占比:collections [1]/[2.5s]
-
GC总耗时:total [1.2s]/[4.5s]
-
内存使用情况:memory [434.1mb]->[352.4mb]/[990.7mb]
-
内存池使用情况:
- young generation (eden) [160.1mb]->[41.8mb]/[266.2mb]
- survivor [16.5mb]->[33.2mb]/[33.2mb]
- old generation [257.4mb]->[277.3mb]/[691.2mb]
通过分析日志,可以看到这次GC总共耗时4.5s,其中young耗时2.5s。
解决办法
提高新生代内存大小,避免在高业务并发时,因频繁GC导致老年代内存过早回收。
新生代内存适合配置的大小约为总堆内存的三分之一。通常新生代内存为堆内存的三分之一。
# 指定堆内存大小
-Xms12g
-Xmx12g
# 指定初生代初始大小
-XX:NewSize=4g
# 指定初生代最大大小
-XX:MaxNewSize=4g
-XX:+UseConcMarkSweepGC
# 启用CMS并行标记(默认开启)
-XX:+CMSParallelRemarkEnabled
# 启用CMS多线程收集(默认开启)
-XX:+CMSConcurrentMTEnabled
# 只使用CMS垃圾回收器来触发垃圾回收。(默认开启)
-XX:+UseCMSInitiatingOccupancyOnly
# 当CMS垃圾回收器老年代使用的堆达到70%时,触发CMS垃圾回收器。
-XX:CMSInitiatingOccupancyFraction=70
检查配置修改结果
在重启ES服务后,可以通过访问以下地址,查看是否生效。
curl -XGET 'http://localhost:9200/_nodes?filter_path=**.jvm.options' | jq '.nodes[].jvm.options'
可以通过以下接口查看GC次数。
http://192.168.230.201:9200/_nodes/stats?filter_path=**.gc