GC垃圾回收算法

Go 语言的垃圾回收器(GC)是为了自动管理内存而设计的,它通过回收不再使用的内存来避免内存泄漏。Go 的垃圾回收器使用了多种算法,逐步演进以提高性能和减少对程序运行的影响。以下是 Go 语言垃圾回收器的主要算法和机制:

1. 标记-清除算法 (Mark-and-Sweep)

概念

  • 标记-清除 是 Go 语言垃圾回收的核心算法之一。该算法分为两个阶段:标记(Mark)和清除(Sweep)。
  • 标记阶段:遍历所有的对象,从根对象开始,标记所有可达的对象(即仍然在使用的对象)。
  • 清除阶段:遍历堆中所有的对象,清除那些没有被标记为可达的对象,将它们的内存归还给系统。

过程

  1. 根集合的确定:根集合包括全局变量、当前在栈上的局部变量、以及 CPU 寄存器中的指针。
  2. 标记阶段:从根集合开始,递归地遍历所有可达对象,并将其标记为“存活”。
  3. 清除阶段:扫描整个堆内存,释放未被标记为“存活”的对象的内存。

2. 三色标记算法 (Tri-Color Marking)

概念

  • 三色标记算法 是对标记-清除算法的优化,解决了标记阶段与程序并发运行时的冲突。通过将对象分为三类(白色、灰色、黑色)来处理标记过程。

三色划分

  • 白色:尚未访问过的对象。在标记阶段开始时,所有对象都是白色的。
  • 灰色:已访问但其引用的对象还未标记的对象。
  • 黑色:对象本身及其引用的所有对象都已访问并标记。

过程

  1. 初始状态:所有对象都是白色,根对象变成灰色。
  2. 标记阶段:从灰色对象开始,将它们引用的对象变成灰色,并将自身变成黑色,直到所有灰色对象都被处理完毕。
  3. 清除阶段:白色的对象就是未被访问到的对象,可以被安全地回收。

优点

  • 该算法允许程序在标记阶段继续运行,通过将程序对内存的修改加入到标记过程中,避免了数据不一致的问题。

3. 写屏障 (Write Barrier)

概念

  • 写屏障 是在垃圾回收过程中使用的一种机制,用来处理并发情况下的新对象创建和对象引用的改变。写屏障可以帮助保持三色标记算法的正确性。

实现

  • 当程序对某个对象的引用发生变更时,写屏障会记录这种变更并确保被引用的对象状态被正确更新(例如,将被引用的白色对象立即变成灰色)。

作用

  • 写屏障确保在垃圾回收进行时,即使有新的对象被创建或对象之间的引用发生变化,垃圾回收器也能够正确地标记这些对象。

4. 增量式 GC (Incremental GC)

概念

  • 增量式 GC 是一种将垃圾回收过程分成小块来执行的技术,目的是减少 Stop-the-World(STW)时间,使应用程序在垃圾回收时仍然能够继续执行。

过程

  • 垃圾回收的标记阶段被分割成多个小的步骤,这些步骤可以与应用程序的执行交替进行,从而降低一次性 STW 的时间长度。

5. 并发标记清除 (Concurrent Mark-and-Sweep)

概念

  • 并发标记清除 是对传统标记-清除算法的进一步优化,允许标记阶段和应用程序并发执行,减少了垃圾回收对应用程序响应时间的影响。

过程

  1. 初始标记:STW,标记根对象。
  2. 并发标记:程序和垃圾回收器并发运行,标记堆中的存活对象。
  3. 重新标记:短暂的 STW,确保并发标记过程中未处理的对象被正确标记。
  4. 并发清除:并发清除未被标记的对象的内存。

6. 压缩 (Compaction)

概念

  • 压缩 是一种减少内存碎片的技术。在某些垃圾回收器中,清除阶段不仅回收了未使用的对象,还将存活对象移动到内存的连续区域,避免内存碎片化。
  • Golang 的垃圾回收器目前不主动进行内存压缩,但在特定场景下可能会通过内存分配策略减少碎片。

7. 垃圾回收参数调优

  • GOGC 环境变量:用于控制垃圾回收的频率,默认值为 100,表示堆内存每增长 100% 触发一次垃圾回收。增大该值可以减少垃圾回收频率,但会占用更多内存。
  • GODEBUG 变量:可以通过设置 GODEBUG=gctrace=1 来开启 GC 日志,分析 GC 运行情况。

8. 总结

Go 的垃圾回收器通过标记-清除算法、三色标记、写屏障、增量式回收和并发标记清除等技术,确保了高效的内存管理,同时尽量减少了对应用程序运行的影响。通过合理的垃圾回收策略和调优,开发者可以在性能和内存使用之间找到平衡点。