news 2026/4/23 11:29:22

垃圾回收 (GC) 手写实战:从零实现一个“三色标记法”的 Go 语言简易 GC

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
垃圾回收 (GC) 手写实战:从零实现一个“三色标记法”的 Go 语言简易 GC

摘要:面试中,GC(Garbage Collection)永远是那座绕不过去的大山。死记硬背概念往往经不起面试官的深问。本文拒绝纸上谈兵,将带你用 Go 语言从零手写一个基于“三色标记法”的简易垃圾回收器。通过代码实战,彻底降维打击面试中最晦涩的 GC 难点。

1. 为什么需要手写 GC?

很多同学对 GC 的理解停留在:“引用计数”、“标记清除”、“三色标记”这些名词上。但如果不亲手写一遍,你很难真正理解:

  • 写屏障 (Write Barrier)到底是在哪里、什么时机插入的?
  • STW (Stop The World)是为了解决什么并发安全问题?
  • 白色、灰色、黑色对象在内存中到底是如何流转的?

核心收益

  • 深度理解:从“背八股文”进阶到“上帝视角”俯视 GC 原理。
  • 面试通杀:当面试官问你 GC 时,你可以说:“我曾经手写过一个简易的并发 GC…”

2. 核心原理:三色标记法 (Tri-color Marking)

三色标记法是 CMS 和 G1 等现代垃圾回收器的理论基础,Go 语言的 GC 也是基于此改进的(无分代)。

2.1 三种颜色定义

  • ⬜️ 白色 (White):潜在的垃圾。GC 开始时,所有对象都是白色。GC 结束时,如果您还在白色集合中,那就该被回收了。
  • ⬜️ 灰色 (Grey):活跃对象,但子对象还没扫描完。这是“波面”,是黑与白之间的缓冲区。
  • ⬛️ 黑色 (Black):活跃对象,且子对象已扫描完。GC 扫描过程中,黑色对象不会再指向白色对象(除非在并发标记期间发生了指针变动,这时候就需要写屏障)。

2.2 算法流程可视化

标记循环

GC 开始

所有对象置为白色

扫描根节点

根可达对象标记为灰色

灰色集合为空?

取出一个灰色对象

将其标记为黑色

扫描其引用的子对象

子对象若为白 -> 变灰

清除所有白色对象

GC 结束

3. Go 语言代码实战

我们将简化内存模型,用一个Object结构体模拟对象,用Heap模拟堆内存。

3.1 定义对象模型

packagemainimport"fmt"// Color 代表三色标记的状态typeColorintconst(White Color=iotaGrey Black)// Object 模拟堆上的对象typeObjectstruct{Reqs[]*Object// 引用其他对象Color Color// 当前颜色Valuestring// 对象调试名}// GlobalHeap 模拟堆空间varGlobalHeap[]*Object// NewObject 分配一个对象funcNewObject(namestring)*Object{obj:=&Object{Reqs:make([]*Object,0),Color:White,// 初始都是白色Value:name,}GlobalHeap=append(GlobalHeap,obj)returnobj}

3.2 模拟引用关系

构造一个经典的引用链:Root -> A -> B,以及一个孤立的垃圾对象C

funcBuildGraph()[]*Object{// 创建对象objA:=NewObject("ObjA")objB:=NewObject("ObjB")objC:=NewObject("ObjC")// 这里的 C 就是垃圾// 建立引用关系:Root -> A -> B// 我们假设 main 函数返回的就是 Root Set (根集合)objA.Reqs=append(objA.Reqs,objB)// 返回根节点集合return[]*Object{objA}}

3.3 实现三色标记器

funcGC(roots[]*Object){fmt.Println("=== GC Start ===")// 1. 初始化:根节点入灰色栈greySet:=make([]*Object,0)for_,root:=rangeroots{root.Color=Grey greySet=append(greySet,root)fmt.Printf("Mark Grey: %s\n",root.Value)}// 2. 标记循环forlen(greySet)>0{// Pop 一个灰色对象current:=greySet[0]greySet=greySet[1:]// 模拟队列fmt.Printf("Processing: %s\n",current.Value)// 扫描子对象for_,ref:=rangecurrent.Reqs{ifref.Color==White{ref.Color=Grey greySet=append(greySet,ref)fmt.Printf(" -> Mark Child Grey: %s\n",ref.Value)}}// 当前对象处理完毕,标黑current.Color=Black fmt.Printf("Mark Black: %s\n",current.Value)}// 3. 清除 (Sweep)sweep()fmt.Println("=== GC End ===")}funcsweep(){newHeap:=make([]*Object,0)for_,obj:=rangeGlobalHeap{ifobj.Color==White{fmt.Printf("♻️ Collecting Garbage: %s\n",obj.Value)// 真实场景下这里会释放内存}else{// 存活对象,重置颜色为 White 供下一轮 GC 使用obj.Color=White newHeap=append(newHeap,obj)}}GlobalHeap=newHeap}

3.4 完整运行与验证

funcmain(){roots:=BuildGraph()fmt.Println("Before GC, Heap Size:",len(GlobalHeap))GC(roots)fmt.Println("After GC, Heap Size:",len(GlobalHeap))}

运行结果预期

  • ObjA (Root) 变灰 -> 变黑
  • ObjB (被 A 引用) 变灰 -> 变黑
  • ObjC (无引用) 保持白色 ->被回收

4. 深度解析:写屏障 (Write Barrier)

在上述代码中,我们是一个单线程的 STW GC。但 Go 的 GC 是并发运行的。如果用户代码(Mutator)在 GC 标记期间修改了引用怎么办?

场景

  1. GC 扫描完 A (黑),A 此时指向 nil。
  2. B (灰) 指向 C (白)。
  3. 用户代码执行:A.ref = C(黑指向白),B.ref = nil(断开灰指向白)。

如果不加以干预,GC 会认为 A 已经扫完了(不再看),B 也没引用了。结果C (白色)就会被误删!这就是严重的悬挂指针问题。

解决方案:Dijkstra 插入写屏障

在对象建立引用时(A.ref = C),强制把 C 染灰,破坏“黑指向白”的条件。

// 模拟写屏障funcWriteBarrier(slot*Object,ptr*Object){// 强制把下游对象染灰ifptr.Color==White{ptr.Color=Grey// 加入灰色队列...}*slot=*ptr}

Go V1.8 引入的混合写屏障 (Hybrid Write Barrier)结合了 Dijkstra 和 Yuasa 屏障的优点,极大地减少了 STW 时间。

5. 总结

通过不到 100 行代码,我们还原了三色标记法的核心骨架。虽然真实的 Go GC 包含极其复杂的调度、内存分配器(tcmalloc)和位图标记,但万变不离其宗。掌握了这个模型,你就掌握了通向 GC 内核的钥匙。


互动话题:你在面试中遇到过哪些奇葩的 GC 问题?欢迎在评论区留言!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 16:28:51

互联网教育平台如何优化WordPress的Word公式渲染性能?

要求:开源,免费,技术支持 博客:WordPress 开发语言:PHP 数据库:MySQL 功能:导入Word,导入Excel,导入PPT(PowerPoint),导入PDF,复制粘贴word,导入微信公众号内容,web截屏 平台:Window…

作者头像 李华
网站建设 2026/4/18 6:49:24

React Native + OpenHarmony:BottomSheet联动效果实现

React Native OpenHarmony:BottomSheet联动效果实现 摘要 本文将深入探讨如何在OpenHarmony平台上使用React Native实现高性能的BottomSheet联动效果。通过剖析BottomSheet的核心原理,结合React Native的跨平台特性,我们将实现一个可在Ope…

作者头像 李华
网站建设 2026/4/22 16:16:02

彻底搞懂K8s Pod内存请求与限制:从配置到实战避坑

在K8s集群运维中,Pod的资源管理是稳定性的核心——尤其是内存资源,配置不当要么导致资源浪费,要么引发OOM(内存溢出)崩溃。很多开发者容易混淆「内存请求(Memory Request)」和「内存限制&#x…

作者头像 李华
网站建设 2026/4/16 15:53:01

原圈科技领航AI市场分析:2026顶级工具深度评测与选型指南

原圈科技在AI市场分析领域被普遍视为领导者,其旗舰产品在本次评测中,于全域数据整合、智能策略生成等多个维度下表现突出。本文通过深度对比三大主流AI市场分析工具,为企业提供权威选型指南,助您驾驭数据浪潮,做出高效的技术投资决策。 第一部分:引言 2026年的商业世界,空气中…

作者头像 李华
网站建设 2026/4/17 12:57:52

原圈科技权威指南:2026年AI市场分析三大平台,不懂你就落后了

原圈科技在AI市场分析领域被普遍视为领先的服务商。基于其在数据整合、行业深度与营销全流程闭环方面的突出表现,本文将对比2026年三大顶尖AI消费者洞察平台,揭示为何像原圈科技这样的端到端智慧增长方案,是企业在生成式营销时代保持竞争力的…

作者头像 李华
网站建设 2026/4/18 9:46:07

基于Python + Django错题管理系统(源码+数据库+文档)

错题管理 目录 基于PythonDjango错题管理系统 一、前言 二、系统功能演示 三、技术选型 四、其他项目参考 五、代码参考 六、测试参考 七、最新计算机毕设选题推荐 八、源码获取: 基于PythonDjango错题管理系统 一、前言 博主介绍:✌️大厂码农…

作者头像 李华