Go三色标记清除是分阶段并发GC,通过写屏障保障强三色不变式,STW仅限根扫描与栈重扫,清除异步按页进行,延迟压至百微秒级。

Go 的三色标记清除(Tri-color Marking + Sweep)不是一次性停顿扫描,而是一个分阶段、带写屏障保护的并发回收流程。它核心目标是把 STW(Stop-The-World)压缩到毫秒级,同时不丢对象、不误回收。
三色状态的本质含义
所谓“三色”,是逻辑抽象,并非真实存储颜色值:
- 白色:初始默认状态,表示“尚未确认是否可达”;GC 结束后仍为白色的对象即为垃圾,会被清除
- 灰色:已确认可达,但其引用的对象还没被扫描;它们被存放在一个标记队列(gcWork)中,等待处理
- 黑色:已完全扫描完毕,该对象及其所有直接/间接引用都已纳入标记范围;本次 GC 中不会被回收
标记阶段的四步主流程
标记不是全堆扫一遍,而是从根集合出发,逐步推进灰色边界,直到灰色队列清空:
- 将所有根对象(全局变量、各 goroutine 栈上指针、寄存器等)标记为灰色,并入队
- 从队列取一个灰色对象,扫描它持有的所有指针字段
- 对每个指针指向的白色对象,将其标记为灰色并入队
- 当前对象自身标记为黑色;重复直到队列为空
此时所有可达对象非黑即灰(灰是过渡态),白色对象即为不可达垃圾。
为什么需要写屏障?防止对象丢失
因为标记和用户代码并发执行,若不加干预,可能出现“黑色对象引用新创建的白色对象”,而该白色对象又无其他灰色路径可达——它就会被漏掉、误回收。这就是违反强三色不变式。
Go 自 1.8 起采用混合写屏障(hybrid write barrier),在指针赋值时插入检查逻辑:
- 当发生
A.field = B,且 A 已是黑色、B 是白色时,强制将 B 标记为灰色 - 该机制覆盖堆上写操作,配合最后的 STW 栈重扫描,确保栈上新产生的指针也被捕获
清除阶段如何做到几乎无停顿
清除不再像早期那样遍历整个堆,而是按页(page)异步清理:
- 标记结束后,白色对象的内存页被标记为“可回收”,但不立即归还 OS
- 后续内存分配时,优先复用这些页;真正释放给操作系统由后台线程按需完成
- Go 1.19 引入软内存限制(
GOMEMLIMIT),让清除更主动响应内存压力
因此,清除本身基本不造成用户可观测延迟,真正的 STW 只发生在标记开始前的根扫描准备,以及标记结束后的栈重扫描,通常控制在百微秒级。








