问题
线上容器 JVM 频繁 OOM。先后有三个不同的现象
-
频繁FullGC
-
原因:同类型的对象数很多,没有被GC掉
-
解决:99线监控积累了很多guage数据,主要是请求处理慢,导致请求的guage数据堆积
-
-
Major GC很慢
-
原因:有大对象,一直积攒
-
解决:mongo查询代码有坑,查询失败,导致查询的监控数据堆积在内存没有释放。监控使用k8s promethus请求监控,监控组件也有坑,没有超时释放监控数据。
-
-
Minor GC很慢
-
原因:年轻代内存占用很多,朝生夕死的对象很多
-
解决:入参验证逻辑前置,无效的输入尽早排除,减少对象创建
-
定位步骤
-
查看gc日志
-
从日志分析可能的内存泄露
-
工具:https://gceasy.io/gc-index.jsp
-
结论
-
堆内存没有清理
-
频繁FullGC
-
老年代内存持续上涨
-
-
-
arthas分析实时内存
-
工具:https://arthas.aliyun.com/doc/vmtool.html, 用memory命令查看内存
-
结论:老年代内存一直没清理干净
-
-
内存dump
-
问题:用公司的在线内存分析工具, dump内存。但是内存数据大,有8G。手工从容器下载速度很慢而且影响线上服务。
-
解决:dump出内存文件后,把容器摘流,使用在线分析工具,把内存文件上传到分析平台
-
结论:能看出是哪个类型占用了内存,但是无法定位对象的引用链
-
内存分析
-
工具:JProfiler
-
问题:在线分析内容比较少,从分析平台上下载dump文件,加载到JProfiler分析
-
结论
-
监控平台很多请求监控数据缓存在内存中,没有清理。根本原因是服务的请求QPS上涨到原来的3倍,且请求rt升高,导致很多请求的监控数据堆积在内从中
-
-
优化方案
-
降低请求rt:优化mongo, redis读写速度
-
关闭不必要的监控:95线,99线监控会占用很多内存。这些监控默认是打开的,修改配置关闭这些监控。
结果
-
线上观察2周,未发现新的OOM问题。内存回收正常,水位较低。
-
FullGC 优化结束后,虽然水位降低,但是请求量大的时候,内存依然比较紧张,且GC耗时严重,继续进行major/min GC耗时优化。
-
完成major/minGC耗时优化后,GC年轻代内存分配从原先的400M/s下降到60M/s, minor GC 从秒级下降到ms级, 次数从15次+降到2次. 年轻代占用从1G下降到300M;major GC耗时从秒级下降到ms级,总内存占用从50%+下降到个位数。