Skip to content

垃圾回收

  • Golang中采用 三色标记清除算法(tricolor mark-and-sweep algorithm) 进行GC。

一些名词

垃圾内存

  • 程序中不再被引用的对象(即 “垃圾”),释放其占用的内存,供新对象使用。

内存泄漏

  • 程序在分配了内存但不再需要使用时,却未能正确释放这块内存空间,导致这部分内存被占用且无法再次使用,从而造成系统内存的浪费和性能下降。 这种问题会随着程序运行积累,最终可能导致程序甚至整个系统崩溃。

内存溢出

  • 程序在申请内存时,无法满足当前需求,导致内存分配失败。

GC 的基本原理

标记(Mark)

  • 从 “根对象”(如全局变量、当前调用栈中的变量等)出发,遍历所有可达的对象(被引用的对象),标记为 “存活”。

清除(Sweep)

  • 遍历整个内存空间,回收所有未被标记(即 “不可达”)的对象,释放其占用的内存。

压缩(Compact,可选):

  • (可选)在清除阶段后,将所有存活的对象移动到内存的一端,形成连续的内存块,减少内存碎片。

Go 语言的 GC 特点

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

  • 标记阶段与用户程序并发执行(不阻塞用户代码),仅在标记开始和结束时需要短暂的 “stop the world”(STW)暂停,极大降低延迟。
  • 清除阶段也与用户程序并发执行,不会阻塞用户代码的运行。

三色标记法

  • 白色:未被标记(潜在垃圾)。
  • 灰色:已被标记,但引用的子对象未完全处理。
  • 黑色:已被标记,且引用的子对象均已处理(存活对象)。
  • 通过这种方式高效追踪可达对象,避免全量扫描的性能开销。

写屏障(Write Barrier)

  • 在并发标记阶段,若用户程序修改对象引用(如将一个白色对象赋值给黑色对象),写屏障会拦截操作并将新引用的对象标记为灰色,防止漏标记。

三色标记法

初始化(初始标记,STW 阶段)

  • 触发时机GC 开始时,首先进入短暂的 “Stop The World”(STW)阶段,暂停所有用户协程(Goroutine)。
  • 操作:标记所有从根对象直接引用的对象为灰色。
    • 将所有对象初始化为白色。
    • 从 “根对象”(Root Objects,如全局变量、当前调用栈中的局部变量、寄存器中的对象等)出发,将直接可达的对象标记为灰色,加入 “灰色队列”(待处理队列)。
  • 目的:快速标记根对象直接引用的对象,为后续并发标记做准备。此阶段 STW 时间极短(微秒级)。

并发标记(与用户程序并行)

  • 操作
    • 恢复用户协程运行,同时 GC 后台协程开始处理 “灰色队列.
    • 从灰色队列中取出一个灰色对象,将其标记为黑色。
    • 遍历该黑色对象引用的所有子对象:
      • 如果子对象是白色,将其标记为灰色,并加入灰色队列。
      • 如果子对象是灰色,忽略。
      • 如果子对象是黑色,忽略。
    • 重复以上过程,直到灰色队列为空(理论上所有可达对象都已被标记为黑色)。
  • 关键问题:并发标记时,用户程序可能修改对象引用(如将一个白色对象的引用赋值给黑色对象),导致该白色对象被遗漏标记(最终被误判为垃圾)。
  • 解决方案:通过写屏障(Write Barrier) 拦截对象引用的修改操作,确保新引用的白色对象被标记为灰色,避免漏标。