YUANJI666 j4 FUN

Back

GO实现原理-垃圾回收Blur image

本篇文章仅记录自己对Go垃圾回收原理的理解,待日后学习更加深入,再补上源码的分析 参考了多篇博客和Aceld老师的视频讲解(非常生动,推荐观看)

前言#

GC(garbage collection)垃圾回收,是一个内存管理策略。c/c++把内存管理全权交给程序员,而Go/Java等现代编程语言,语言本身自带一个垃圾回收器运行在后台,按照既定策略为用户回收不再使用的对象,释放对应的内存空间。

优缺点:

  • 屏蔽内存细节:开发人员更好的聚焦业务逻辑
  • 全局视野:现代软件开发通常为团队协作而成,个人只负责一个模块,协作时进行内存管理的负担太大,这时候Garbage Collector类似有“全局视野”
  • 性能成本:会用额外空间存储必要的内存信息,还会有时中断程序运行来支持GC工作,拖慢程序

总的来说,除了极端追求性能的场景,GC带来的好处是远远大于坏处的,更安全,交付效率更高。

直击要害#

网络上很多博客都从漫长的历史讲起,我们不妨先搁置,直击要害,看看GO-GC的核心算法

Go自V1.8之后定型为三色标记+混合写屏障

名字很唬人,其实理解起来很容易

三色标记#

三色标记过程

三色即黑,灰,白

  • 黑:已访问,直接引用的对象也都访问过
  • 灰:已访问,但是直接引用对象没有访问
  • 白:未访问,是潜在的垃圾对象

进行一轮GC,就是不断广度优先遍历整个变量引用图的过程,当灰色为空时,回收白色,就达到了我们的目的

混合写屏障#

所谓混合写屏障,就是混合了插入写屏障和删除写屏障

可以把屏障理解为拦截的作用,其本质是对象赋值前插入的一段代码

真实场景中,需要补充一个新的设定——屏障机制无法作用于栈对象.

这是因为栈对象可能涉及频繁的轻量操作,倘若这些高频度操作都需要一一触发屏障机制,那么所带来的成本将是无法接受的.

为什么要有屏障?#

是为了满足三色不变性,满足了三色不变性,就保证了并发条件下的三色标记机制的正常工作

三色不变性:

  • 强三色不变性:黑色对象不可以直接引用白色对象
  • 弱三色不变性:当黑色对象直接引用白色对象时,必须有另一个灰色对象可以直接或间接访问到该灰色对象,称作受到灰色对象的保护

屏障怎样满足三色不变性?#

混合写屏障机制:

  •  GC 开始前,以栈为单位分批扫描,将栈中所有对象置黑
  •  GC 期间,栈上新创建对象直接置黑
  •  引用新对象,新对象置灰(满足强不变性)
  •  删除引用对象,被删除置灰(满足弱不变性)(我们不知道被删除上游是否有保护)

混合写屏障

以史为鉴#

三色标记VS标记-清除#

标记-清除法只有黑,白两种颜色,进行一次遍历之后,回收白色的节点

三色标记的几个核心优势:

  • 增量回收:标记-清除法一次STW(stop the world)必须完成所有标记工作,因为缺乏中间态,而三色标记每次只需遍历灰色节点,可以一步一步,增量回收
  • 并发标记:标记-清除法不支持屏障操作

我们可以想象,标记清除算法中,标记中途黑色变量引用白色,此时白色根本不可能遍历到

本质:三色标记通过引入“灰色”这一中间状态,将标记过程从原子操作转变为可分割、可追踪的过程,从而实现了标记阶段与应用线程的并发执行,这是其最根本的优势。

混合写屏障VS插入/删除写屏障#

在屏障不能作用于栈的前提下,单纯插入/删除都需要引入STW,最后对栈进行兜底

而在混合机制下,任何涉及到堆的变量,都会屏障保护,避免了STW

如果都是栈呢?

栈在GC开始会分批变黑,如果此时出现了一个栈黑引用白,另一个栈白删除引用白?

这是个伪命题,详细看小徐先生的编程世界case4

还有STW吗?#

deepseek: 尽管采用了高并发的三色标记,但为了确保垃圾回收的正确性和简单性,在以下两个关键节点仍需要短暂的全局停顿:

  1. 标记开始阶段(Mark Setup - STW):主要任务是开启写屏障。为了确保所有协程(goroutine)都能感知并启用写屏障,必须让它们短暂地暂停在一个安全点,这个操作非常快
  2. 标记终止阶段(Mark Termination - STW):主要任务是关闭写屏障,并执行一些最终的状态检查和清理工作。选择在此阶段使用一个短暂的STW,可以简化设计并保证逻辑正确,而带来的性能开销极小

怎样编写GC友好代码?#

为了进一步降低GC的整体开销和潜在影响,你可以:

  • 减少堆上对象的分配:尽量使用栈分配或通过对象池(如 sync.Pool)复用对象,减少GC的扫描压力
  • 避免在循环中创建大量临时对象:这会增加GC的触发频率和标记辅助(Mark Assist)工作
  • 合理设置 GOGC 参数:该值(默认100)决定了触发下一轮GC的堆内存增长比例。适当调高可降低GC频率(以占用更多内存为代价),反之亦然。

总结#

Go的GC是一个追求极致低延迟的并发回收器。虽然理论上无法彻底消除STW,但它通过精妙的设计,已将STW时间压缩到绝大多数应用都难以感知的极短范围,这也是三色标记算法在工程实践上取得巨大成功的体现。