Golang 垃圾回收(Garbage Collection)机制

110 阅读4分钟

Go 语言垃圾回收机制详解

1. 垃圾回收的目标

  • 自动管理内存:开发者无需手动分配和释放内存。
  • 避免内存泄漏:回收不再使用的内存,防止程序内存无限增长。
  • 提高开发效率:减少内存管理带来的复杂性和错误。

2. 垃圾回收器设计

Go 语言的垃圾回收器是一种并发、三色标记清除(Concurrent Tri-color Mark and Sweep) 的垃圾回收器。它结合了标记清除(Mark and Sweep)三色抽象(Tri-color Abstraction) 算法,并支持并发执行,减少对程序性能的影响。

2.1 三色标记清除法

三色标记清除算法将内存中的对象分为三种颜色:

  • 白色:尚未被访问的对象(潜在可回收对象)。
  • 灰色:已被访问,但其引用的对象还未被完全检查。
  • 黑色:已被访问且其引用的对象也已被检查(不可回收对象)。
工作流程
  1. 标记阶段

    • 从根对象(如全局变量、栈变量等)开始,将所有可达对象标记为灰色。
    • 遍历灰色对象,将其引用的对象标记为灰色,自身标记为黑色。
    • 重复上述过程,直到没有灰色对象为止。
  2. 清除阶段

    • 回收所有白色对象(未被标记的对象)。

根对象包括

  • 全局变量:程序中定义的全局变量。
  • 栈变量:当前所有 goroutine 的调用栈中的局部变量。
  • 寄存器中的对象:CPU 寄存器中存储的对象引用。
  • 其他根对象:如运行时的特殊数据结构(如 goroutine 的上下文、定时器等)。
可达对象的判断

从根对象出发,通过引用关系遍历所有对象:

  • 如果一个对象被根对象直接引用,那么它是可达的。
  • 如果一个对象被可达对象引用,那么它也是可达的。

2.2 并发垃圾回收

Go 的垃圾回收器是并发的,即垃圾回收的大部分工作可以与应用代码同时运行。具体实现包括:

  • 并发标记:标记阶段与应用代码并发运行。
  • 写屏障(Write Barrier):在标记阶段,如果应用代码修改了对象的引用关系,写屏障会确保被修改的对象被正确地标记。
写屏障的作用

写屏障是一种在对象引用关系被修改时触发的机制,它会确保被修改的对象被正确地标记为可达状态。当应用程序修改一个对象的引用关系时,写屏障会拦截该操作并确保垃圾回收器能够正确地更新标记信息。

3. 垃圾回收的触发条件

  • 内存分配达到阈值:当堆内存的使用量达到一定比例时,会触发垃圾回收。
  • 手动触发:调用 runtime.GC() 可以强制触发垃圾回收。

4. 垃圾回收的特点

  • 低延迟:通过并发标记和清除,尽量减少垃圾回收对程序性能的影响。
  • 可配置性:通过环境变量(如 GOGC)可以调整垃圾回收的行为。GOGC 的默认值为 100,表示当堆内存增长到上一次垃圾回收后的 2 倍时触发垃圾回收。
  • 优先回收短期对象:虽然 Go 的垃圾回收器没有严格的分代模型,但其标记清除算法会优先回收短期对象。通过优先处理灰色对象(通常是短期对象),垃圾回收器能够更快地识别和回收不可达的短期对象。

5. 调试与监控

  • 调试信息:设置环境变量 GODEBUG=gctrace=1 可以打印垃圾回收的详细日志。
  • 性能分析:使用 pprof 工具可以分析垃圾回收对程序性能的影响。

6. 最新版本的变化

在 Go 1.18 及之后的版本中,垃圾回收器引入了混合写屏障

  • 标记阶段:混合写屏障在标记阶段同时使用多种屏障技术,例如在堆上使用一种屏障,在栈上使用另一种屏障,优化了对象的标记过程。
  • 扫描阶段:通过并行标记和并发扫描,减少了单线程标记带来的性能瓶颈,提高了整体效率。
  • 栈上对象处理:混合写屏障优化了栈上对象的处理,减少了不必要的标记和扫描,提高了回收效率。