Appearance
垃圾回收
- 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) 拦截对象引用的修改操作,确保新引用的白色对象被标记为灰色,避免漏标。
