Golang自1.5版本开始引入三色GC,多次改进,GC停顿时间降低到1ms。
一:GC的触发
GC的触发条件有以下几种:
- gcTriggerAlways:强制触发GC
- gcTriggerHeap:当前分配的内存达到一定值就触发GC
- gcTriggerTime:当一定时间没有执行过GC触发
- gcTriggerCycle:要求启动新一轮的GC,已启动则跳过,手动触发GC的runtime.GC()会使用这个条件
其中gcTriggerHeap和gcTriggerTime这两个条件是自然触发的。
gcTriggerHeap的判断依据为:
memstats.heap_live >= memstats.gc_trigger //heap_live is the number of bytes considered live by the GC
其中memstats.gc_trigger的计算公式是:
trigger = unit64(float64(memstats.heap_marked)*(1+triggerRatio))//heap_marked is the number of bytes marked by the previous GC
tiggerRatio 与goalGrowthRatio正相关
goalGrowthRatio = float64(gcpercent)/100
公式中的goalGrowthRatio“目标Heap增长率”通过设置环境变量GOGC(gcpercent)调整,默认值为100。
gcTriggerTime的判断依据为:
t.now - lastgc > forcegcperiod
其中forcegcperiod的定义是2分钟,也就是2分钟没有执行GC就会强制触发。
二:如何进行GC优化
2.1:硬性参数GOGC
比如当前程序使用4M堆内存,即memstats.heap_marked内存为4M,当程序占用的内存上升到memstats.heap_marked*(1+GOGC/100)=8M时候,gc就会被触发,开始进行相关的gc操作。
如何对GOGC的参数进行设置,要根据生产情况中的实际场景来定。增加它的值可以减少GC触发但需保证内存足够大。如果你的内存少,只能更频繁的GC以节省内存,那么降低GOGC值。设置GOGC=off可以彻底关掉GC
2.2:减少对象分配
内存复用,减少对象申请。下面的样例代码中实现内存复用,其关键是一个缓存的管道buffer,可存储5个字节数组,当程序需要一个字节数组时候,优先使用select从缓存的管道中去取。最终内存池中的内存和从操作系统请求的内存很接近,堆中只有很少量的未使用内存最终返还给操作系统。
package main
import (
"fmt"
"math/rand"
"runtime"
"time"
)
func makeBuffer() []byte {
return make([]byte, rand.Intn(5000000)+5000000)
}
func main() {
pool := make([][]byte, 20)
buffer := make(chan []byte, 5)