essay
Go的垃圾回收机制
#go
关于Go的垃圾回收
核心结论:三色标记法 + 混合写屏障 + 并发标记清除 GC,全程几乎不卡顿(低延迟)。
垃圾回收的几种情况:
- 堆内存增长到阈值触发(90% 场景):
当前堆使用大小 ≥ 上次 GC 后堆大小 × 触发比例
默认触发比例:GO GC = 100(意思:堆增长 100% 就 GC)
举例:
- 上次 GC 后,堆用了 100MB
- 堆继续增长,到 200MB
- 达到阈值 → 自动触发 GC
-
每 2 分钟强制触发一次(兜底):
Go 有个后台守护线程,强制规则:
如果超过 2 分钟没有触发过 GC → 直接强制 GC 一次
为什么要这样?
防止你内存占用很低、一直不增长,垃圾永远不回收,占着内存不释放 -
手动触发:代码里主动调用:
runtime.GC()什么时候用?
- 测试
- 内存峰值后主动释放
- 低功耗场景
- 特殊情况:GC 后台运行模式触发:
Go 1.17+ 引入 后台 GC 模式:
- 当程序空闲时
- 系统资源充足时
- 会悄悄跑 GC,整理内存
这是属于优化,不影响业务。
需要注意的是:对象变成垃圾时并不会立刻回收,而是要等到下一次GC时才会回收!
GC的工作流程
整个GC大概可以分为4个步骤:
- 标记准备:
- 开启
写屏障 - 需要极短 STW(微秒)
写屏障的作用:当 GC 在并发标记时,如果你修改了对象引用(a=b),那么它会通知 GC 堆上对象的引用关系发生了变化,从而保证 GC 标记的正确性,不会让 GC 回收了正在使用的对象。
STW:是指Stop the world,早期垃圾回收必须冻结整个程序,影响业务,现在的STW几微秒~几十微秒,人完全感受不到,业务也几乎没影响。
- 并发标记
这是整个 GC 的核心阶段!
- GC 和用户代码同时运行
- GC 扫描所有正在使用的对象
- 标记为 “存活”
- 不暂停业务!
- 标记终止
- 关闭写屏障
- 极短 STW(微秒)
- 并发清除(Concurrent Sweep)
- GC 把没标记的对象(垃圾)回收
- 依然和业务代码并行
- 完全不卡顿
三色标记法(核心)
Go 用 三色 描述对象状态:
白色: 未被访问过 → 是垃圾,会被回收灰色: 正在扫描 → 还有子对象没扫完黑色: 扫描完成 → 存活对象,不回收
标记流程
- 一开始 所有对象都是白色
- 从根对象(栈、全局变量)开始,标记为 灰色
- 遍历灰色对象,把它引用的对象变灰,自己变 黑色
- 直到没有灰色对象
- 剩下的白色对象 = 垃圾 → 清除
注意:哪怕一个对象它没有引用的对象,扫描到它时也会标记为黑色。