go

Go 系列 GC优化

Posted by lichao modified on November 15, 2021

相关指标

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

see more..

适用场景:

  1. 对于无本地缓存常驻、有大量临时对象的服务,对象生存周期较短,gc 效率很高,会导致 gc 内存上限变低,增加 gc 频率
  2. GC 频率较高或波动较大(GC 频率越低越好,较优值 <= 5),GC 时延较高或波动较大,CPU 火焰图中 GC CPU占比超过 5%

优化方法

通过火焰图查看 GC 对 CPU 的占用。理想情况这个值在 1% ~ 2% 之间,一般不需要优化。如果超过 5% ,则需要适当调大 GOGC 这个环境变量。 火焰图 火焰图 runtime.gcBgMarkWorker 为 gc 线程堆栈,上图展示 GC CPU 占比 10.99%,远大于 GC 占比建议值。

原生工具 pprof

只需要在启动脚本里面加入一行: export GOGC=200,注意点:

  1. 调大 GOGC 会增加服务对内存的使用量,虽然目前大部分 Go 服务对内存使用都较少,但是还是要根据自己服务对内存使用情况调整。
  2. 调大 GOGC,执行一次 GC,需要扫描的内存更多了,因为 1.6 的 GC 算法优化较好,往往不会增加单 GC 的停顿时间,但是优化时候仍然需要关于服务的 GC 停顿时间。
  3. 优化的时候着重关注 GC 对 CPU 的占用,降到 1%~ %2即可,切不可盲目调大 GOGC 的值。
  4. 最后,还是要关注服务当前的响应时间,在已经优化的服务中,响应时间均有大幅下降。

参考文献