GOGC优化
礼物说本文诣在分享一个最近优化的关于Golang GC相关的问题,以及如何使用golang soft memory limit去控制GOGC频率。分享一下全过程,包括问题的定位,解决的过程以及最终的结果。希望看完这篇文章的小伙伴可以有所收获。
我们的业务CPU使用率一直很高,promethous中go_memstats_gc_cpu_fraction占比达到了5%,于是找了个高峰期pprof抓了一下,发现GC占比异常,scanobject占比达到了20%,就这个问题我们聊一聊GO的GC如何优化,最终降低了2个数量级。
起因
pprof中scanobject占比在20%
promethous中go_memstats_gc_cpu_fraction占比在5%
过程
定位
通过promethous中go_gc_duration_seconds_count发现 GC频率在 80/min
GC的触发条件: 1. 定时 2. 当前内存 = 上一次GC完的内存 * (1+GOGC(环境变量)/100) 3. 手动触发
猜测:常驻内存很小(30m-40m之间),分配速率很快,导致频繁触发next gc
方案
手动设置GOGC环境变量
GOGC改为800
缺点
设置高了容易OOM,设置低了没效果。在服务有明显的峰谷值的时候无法拿捏那个点
每个服务常驻内存不同,内存增长率也不同,需要分别设置,无法自动化
自动调整GOGC环境变量
参考文章
How We Saved 70K Cores Across 30 Mission-Critical Services
通过runtime.SetFinalize去动态的调整GOGC变量
缺点:
runtime.ReadMemStats 可以获取 bytes of allocated heap objects.但是会STW。
docker环境下 /sys/fs/cgroup/memory/memory.limit_in_bytes 可以获取内存使用,但是不准(包含了堆内存,栈内存,以及伴生容器内存)。
在接流的零界点 GOGC变化会非常大导致异常。
升级GO1.19,使用soft memory limit
GOMEMLIMIT = 实例内存的 70% GOGC = off 服务不自动GC 每次内存到70%的时候触发GC
最终效果如下图,GC-CPU占比明显下降 大约在2个数量级 效果明显
结尾
赶紧升GO1.19 吃上 soft memory limit