← 返回首页
go的gc算法
Go 语言的垃圾回收(GC)机制以其低延迟和高并发特性著称。其核心算法是 并发三色标记-清除(Concurrent Mark-Sweep)算法,并配合混合写屏障(Hybrid Write Barrier)来保证并发安全。
以下是 Go GC 算法的核心要点解析:
一、 核心算法:三色标记-清除法
Go 采用追踪法(而非引用计数法),通过给对象打标签来区分存活状态,主要分为三个阶段:
- 白色(White):尚未被 GC 访问过的对象。在标记阶段结束后,仍为白色的对象将被视为垃圾并回收。
- 灰色(Grey):已被 GC 访问,但其引用的子对象还未被完全扫描的对象。它们构成了一个待处理的队列。
- 黑色(Black):已被完全扫描,且其引用的所有子对象都已被处理(标灰或标黑)的对象。它们是确定存活的对象。
工作流程:GC 开始时所有对象为白色。从根对象(如全局变量、协程栈)开始将其标灰,然后不断从灰色队列中取出对象,将其引用的白色对象标灰,自身标黑。直到灰色队列为空,此时剩下的白色对象即为垃圾,进入清除阶段。
二、 并发安全保障:混合写屏障
在并发标记阶段,用户程序(Mutator)可能会修改对象的引用关系,这极易导致“漏标”(即存活对象被误判为白色垃圾)。为了解决这一问题,Go 引入了写屏障机制:
- 混合写屏障(Hybrid Write Barrier):自 Go 1.8 引入,它结合了 Dijkstra 的“插入写屏障”(保护新引用)和 Yuasa 的“删除写屏障”(保护旧引用)的优点。当程序修改指针时,写屏障会同时将旧对象和新对象标记为灰色。这确保了在并发标记期间,无论对象如何被修改,都不会有存活对象被误删,从而极大地减少了 STW(Stop-The-World)的时间。
三、 GC 的四个执行阶段
一次完整的 GC 周期通常包含以下四个阶段:
- 标记准备(Mark Setup):极短的 STW,初始化 GC 任务,扫描栈和全局变量,将根对象标记为灰色。
- 并发标记(Concurrent Marking):后台线程与用户程序并发运行,遍历灰色对象并着色,期间通过写屏障保证数据一致性。
- 标记终止(Mark Termination):再次极短的 STW,完成剩余的标记工作并统计存活对象,准备清扫。
- 并发清扫(Concurrent Sweeping):与用户程序并发执行,回收所有白色对象的内存,归还给堆或缓存。
四、 Go GC 的独特设计原则
与其他语言(如 Java)相比,Go 的 GC 在设计上有几个显著特点:
- 非分代(Non-generational):Go 不对对象按生命周期(如年轻代、老年代)进行划分,所有对象一视同仁。这得益于 Go 优秀的逃逸分析机制,大量短生命周期对象直接在栈上分配,减轻了堆 GC 的压力。
- 非压缩(Non-compacting):Go 在 GC 过程中不会在内存中移动存活对象。这避免了移动对象和更新引用带来的巨大开销,得益于其基于
tcmalloc的内存分配器能有效管理内存碎片。 - 低 STW 停顿:经过持续演进(尤其是 Go 1.5 引入并发标记和 Go 1.8 引入混合写屏障后),Go 的 STW 停顿时间已从早期的秒级大幅缩短至微秒级(< 0.5ms)。