相关指标
Go 的 GC 被设计为成比例触发、大部分工作与赋值器并发、不分代、无内存移动且会主动向操作系统归还申请的内存。因此最主要关注的、能够影响赋值器的性能指标有:
- CPU 利用率:回收算法会在多大程度上拖慢程序?有时候,这个是通过回收占用的 CPU 时间与其它 CPU 时间的百分比来描述的。
- GC 停顿时间:回收器会造成多长时间的停顿?目前的 GC 中需要考虑 STW 和 Mark Assist 两个部分可能造成的停顿。
- GC 停顿频率:回收器造成的停顿频率是怎样的?目前的 GC 中需要考虑 STW 和 Mark Assist 两个部分可能造成的停顿。
- GC 可扩展性:当堆内存变大时,垃圾回收器的性能如何?但大部分的程序可能并不一定关心这个问题。
调优参数GOGC
GOGC
环境变量可以指定触发垃圾回收的比例。当经过垃圾回收之后,以堆栈中剩余对象为基准,当新增对象达到上轮 GC 对象空间的比例(GOGC)之后,会再次触发GC。GOGC 默认值为100。设置 GOGC=off
可以彻底关闭 GC。runtime/debug
包中的 SetGCPercent
函数可以支持运行时动态修改 GC 比例。
- 示例说明:如果一轮 GC 之后剩余占用内存为 A,则当内存增长到
A+A*GOGC%
之后,会再次触发 GC。 - 建议值:100~1000
适用场景:
- 对于无本地缓存常驻、有大量临时对象的服务,对象生存周期较短,gc 效率很高,会导致 gc 内存上限变低,增加 gc 频率
- GC 频率较高或波动较大(GC 频率越低越好,较优值 <= 5),GC 时延较高或波动较大,CPU 火焰图中 GC CPU占比超过 5%
优化方法
通过火焰图查看 GC 对 CPU 的占用。理想情况这个值在 1% ~ 2%
之间,一般不需要优化。如果超过 5%
,则需要适当调大 GOGC 这个环境变量。
runtime.gcBgMarkWorker
为 gc 线程堆栈,上图展示 GC CPU 占比 10.99%,远大于 GC 占比建议值。
只需要在启动脚本里面加入一行: export GOGC=200
,注意点:
- 调大 GOGC 会增加服务对内存的使用量,虽然目前大部分 Go 服务对内存使用都较少,但是还是要根据自己服务对内存使用情况调整。
- 调大 GOGC,执行一次 GC,需要扫描的内存更多了,因为 1.6 的 GC 算法优化较好,往往不会增加单 GC 的停顿时间,但是优化时候仍然需要关于服务的 GC 停顿时间。
- 优化的时候着重关注 GC 对 CPU 的占用,降到
1%~ %2
即可,切不可盲目调大 GOGC 的值。 - 最后,还是要关注服务当前的响应时间,在已经优化的服务中,响应时间均有大幅下降。
参考文献
- Garbage Collection In Go: https://www.ardanlabs.com/blog/2018/12/garbage-collection-in-go-part1-semantics.html
- Golang GC: Golang 垃圾回收发展史 - 《Go学习手册(For learning Go Tutorial)》 - 书栈网 · BookStack
- Go 性能分析手册
- Java GC:https://docs.oracle.com/en/java/javase/11/gctuning/garbage-collector-implementation.html#GUID-23844E39-7499-400C-A579-032B68E53073