Go(三)GC垃圾回收

5、Golang三色标记混合写屏障GC模式全分析 · 语雀本节为重点章节本章节含视频版:视频链接地址:https...https://www.yuque.com/aceld/golang/zhzanb

1. 核心痛点:STW (Stop The World)

在了解三色标记法之前,必须先了解 STW。 最原始的 GC(如 Go 1.0)在清理垃圾时,必须暂停整个程序所有的 Goroutine,不准任何人修改内存,然后慢慢扫描、清理,最后再恢复程序运行。这个全局暂停的时间就是 STW。如果内存很大,STW 会长达几百毫秒甚至几秒,这对高并发 Web 服务和游戏服务器是致命的。

因此,Go GC 演进的终极目标只有一个:将 STW 时间压榨到极致(目前通常在 1 毫秒以内),让 GC 过程和用户的业务代码并发执行。

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

为了实现并发扫描,Go 将内存里的所有对象在逻辑上分为三种"颜色"状态:

  • ⚪ 白色(White): 潜在的垃圾。刚开始扫描时,所有对象都是白色的。如果扫描结束后还是白色,就会被当成垃圾回收掉。

  • 🔘 灰色(Grey): 存活对象。表示该对象已经被 GC 扫描到了,但它引用的其他对象还没被扫描完。灰色相当于一个待处理的工作队列。

  • ⚫ 黑色(Black): 绝对存活对象。表示该对象已经被扫描,并且它引用的所有子对象也都已经被扫描过了。黑色对象是安全的,绝不会被当成垃圾。

扫描工作流:
  1. 起步: 将所有对象标记为白色。

  2. 找根(Root): 从程序的根节点(如全局变量、当前运行 Goroutine 的栈变量)出发,找到它们直接引用的对象,把这些对象涂成灰色

  3. 顺藤摸瓜: 从灰色对象集合中取出一个对象,把它涂成黑色 。然后把它直接引用的所有白色子对象涂成灰色

  4. 循环往复: 重复第 3 步,直到内存里没有任何灰色对象为止。

  5. 一键清空: 此时,内存里只剩下黑色(存活的有用对象)和白色(没人要的孤儿对象)。直接把所有的白色对象清理掉(Sweep)。

3. 致命危机:并发标记的"丢包"问题

既然我们的目标是"并发",也就是 GC 在扫描变颜色的同时,用户的 Goroutine 还在疯狂运行、修改引用关系。这就会导致一个致命 Bug:存活的对象被当成垃圾误杀了。

误杀必须同时满足两个条件:

  1. 一个黑色 对象(已经扫描完不再回头看)突然指向了一个白色对象。

  2. 同时,那个白色 对象原本唯一依赖的灰色父节点,把这段引用给删除了。

结果就是:黑色对象拉住了白色对象,但由于黑色对象不会被二次扫描,这个白色对象永远变不成灰色和黑色,最终在清除阶段被冤死。

4. 终极解法:混合写屏障 (Hybrid Write Barrier)

为了解决并发误杀,Go 在 1.8 版本引入了混合写屏障技术。 "屏障"就像是内存操作的一个"钩子(Hook)"。当用户代码试图修改对象的引用时,会自动触发一小段额外的逻辑。

混合写屏障的精髓在于它破坏了误杀条件:

  • 无论你把一个引用改成指向谁,或者删除了对谁的引用,只要涉及引用的变动,系统都会强制把相关的对象涂成灰色。

  • 这是一种"宁可错杀(留下一点本该清理的浮动垃圾留到下个周期),绝不放过(绝不误杀存活对象)"的保底策略。

借助混合写屏障,Go 的 GC 就可以放心地和用户代码并发运行,只需要在极短的时间内进行 STW(用于开启屏障和做最后的收尾确认),极大地提升了性能

相关推荐
并不喜欢吃鱼1 小时前
一.C++11:统一列表初始化 + std::initializer_list 超详细精讲
开发语言·c++
CHHH_HHH1 小时前
【C++】二叉搜索树全面升级,深度剖析AVL树
开发语言·数据结构·c++·算法·stl
奋斗的小方1 小时前
Java基础篇09(2):项目实战之基于swing的石头迷阵
java·开发语言
Evand J1 小时前
【代码介绍】自适应R的AEKF(自适应扩展卡尔曼滤波)和经典EKF比较,MATLAB例程|三维非线性系统
开发语言·matlab·ekf·自适应·自适应滤波
雪的季节2 小时前
1 个网络线程 + 3 个数据处理线程(完全隔离)
开发语言
风筝在晴天搁浅2 小时前
快手 CodeTop LeetCode 227.基本计算器Ⅱ
java·开发语言
0xDevNull2 小时前
Java实战面试题(一)
java·开发语言
雪的季节2 小时前
C++ 运行时多态 vs 编译时多态
开发语言
chushiyunen2 小时前
php笔记、下载安装等
开发语言·笔记·php