essay

Go的垃圾回收机制

#go

关于Go的垃圾回收

核心结论:三色标记法 + 混合写屏障 + 并发标记清除 GC,全程几乎不卡顿(低延迟)。

垃圾回收的几种情况:

  1. 堆内存增长到阈值触发(90% 场景):
    当前堆使用大小 ≥ 上次 GC 后堆大小 × 触发比例
    默认触发比例:GO GC = 100(意思:堆增长 100% 就 GC)
    举例:
  • 上次 GC 后,堆用了 100MB
  • 堆继续增长,到 200MB
  • 达到阈值 → 自动触发 GC
  1. 每 2 分钟强制触发一次(兜底):
    Go 有个后台守护线程,强制规则:
    如果超过 2 分钟没有触发过 GC → 直接强制 GC 一次
    为什么要这样?
    防止你内存占用很低、一直不增长,垃圾永远不回收,占着内存不释放

  2. 手动触发:代码里主动调用:

runtime.GC()

什么时候用?

  • 测试
  • 内存峰值后主动释放
  • 低功耗场景
  1. 特殊情况:GC 后台运行模式触发:
    Go 1.17+ 引入 后台 GC 模式
  • 当程序空闲时
  • 系统资源充足时
  • 会悄悄跑 GC,整理内存

这是属于优化,不影响业务。

需要注意的是:对象变成垃圾时并不会立刻回收,而是要等到下一次GC时才会回收!

GC的工作流程

整个GC大概可以分为4个步骤

  1. 标记准备:
  • 开启写屏障
  • 需要极短 STW(微秒)

写屏障的作用:当 GC 在并发标记时,如果你修改了对象引用(a=b),那么它会通知 GC 堆上对象的引用关系发生了变化,从而保证 GC 标记的正确性,不会让 GC 回收了正在使用的对象。

STW:是指Stop the world,早期垃圾回收必须冻结整个程序,影响业务,现在的STW几微秒~几十微秒,人完全感受不到,业务也几乎没影响。

  1. 并发标记
    这是整个 GC 的核心阶段!
  • GC 和用户代码同时运行
  • GC 扫描所有正在使用的对象
  • 标记为 “存活”
  • 不暂停业务!
  1. 标记终止
  • 关闭写屏障
  • 极短 STW(微秒)
  1. 并发清除(Concurrent Sweep)
  • GC 把没标记的对象(垃圾)回收
  • 依然和业务代码并行
  • 完全不卡顿

三色标记法(核心)

Go 用 三色 描述对象状态:

  1. 白色: 未被访问过 → 是垃圾,会被回收
  2. 灰色: 正在扫描 → 还有子对象没扫完
  3. 黑色: 扫描完成 → 存活对象,不回收

标记流程

  • 一开始 所有对象都是白色
  • 从根对象(栈、全局变量)开始,标记为 灰色
  • 遍历灰色对象,把它引用的对象变灰,自己变 黑色
  • 直到没有灰色对象
  • 剩下的白色对象 = 垃圾 → 清除

注意:哪怕一个对象它没有引用的对象,扫描到它时也会标记为黑色。

comments如果有不同意见或者补充,直接留在这里。
contact

在别处继续找到我

如果你想聊技术、设计,或者只是打个招呼。

暂未配置外部链接