GOGC优化

2023-04-06 ⏳1.5分钟(0.6千字)

本文诣在分享一个最近优化的关于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

缺点

  1. 设置高了容易OOM,设置低了没效果。在服务有明显的峰谷值的时候无法拿捏那个点

  2. 每个服务常驻内存不同,内存增长率也不同,需要分别设置,无法自动化

自动调整GOGC环境变量

参考文章
How We Saved 70K Cores Across 30 Mission-Critical Services

Go 调用 Java 方案和性能优化分享

GOGCTuner

通过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