Go 语言 GC 原理概述

208 阅读3分钟

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

GC

GC 这个问题,Java 开发者应该十分熟悉, GC 是一种自动管理内存的机制,垃圾回收器尝试回收程序不使用的对象与占用的内存。

GO 语言的 GC

golang-GC 是基于标记-清除 的三色标记法。 黑、灰 、白 在这里插入图片描述

  • 黑色:该对象已经被标记过了,且对象下的属性也全部被标记过了
  • 灰色:该对象已经被标记过了,但该对象下的属性没有完全被标记(GC 需要从对象中寻找垃圾)
  • 白色:该对象没有被标记过(对象垃圾)

简单说明如下: 白色:表示的是不确定对象 灰色:存活对象,子对象待处理 黑色:存活对象

GC过程

其实跟 Java 中的有点类似。

采用的是三色标记法

  • Stack scan,收集根对象(全局变量和 gorountine 栈上的变量)这个阶段会开启
  • Mark: 标记对象,直到标记所有根对象和根对象可达对象。 此时写屏障会记录所有指针的更改(通过 mutator)
  • Mark Termination: 重新扫描部分全局变量和发生更高的栈变更,完成标记,该阶段会 STW(Stop The World)
  • Sweep: 并发清除未标记的对象

简述如下: 1)标记准备(Mark Setup,需 STW),打开写屏障(Write Barrier)

2)使用三色标记法标记(Marking, 并发)

3)标记结束(Mark Termination,需 STW),关闭写屏障。

4)清理(Sweeping, 并发)

标记过程简述如下: 线找到根对象标记成灰色,然后从灰色集合中选取对象,灰色变成黑色, 这样直到没有灰色的需要标记为止。

GC 过程如下

最终回收的是白色对象

  1. GC. Root 根对象标记为灰色
  2. 然后灰色集合中获取对象,将其标记为黑色,将该对象引用到的对象标记为灰色
  3. 重复步骤2, 知道没有灰色集合可以标记为止
  4. 结束后,剩下的没有标记的白色对象即为 GC Roots 不可达,可以进行回收 在这里插入图片描述

三色标记法存在的问题

多标问题-浮动垃圾

假设标记过程中出现如下:

D(黑色)-> E(灰色)-> F(白色)
		    ↓
		  G(白)

假设标记过程中,应用程序断开了 DE 的引用 , 按理论来说 E/F/G 都应该被回收,但是实际上 E 已经被标记为灰色, 将会被当作存活对象继续遍历下去,从灰色变成黑色,F/G 标记为灰色,导致这部分对象将继续存活下去。 这个就感觉产生了 浮动垃圾 问题了。

漏标问题 -悬挂指针

假设标记过程中出现如下:

A(黑)-> B(灰)-> C(白) -> D(白)

正常情况下,D 存在引用,会被标记成黑色, 但是在并行标记过程中,程序也在并发执行。假设用户程序删除了 CD 的引用, 并且使得。A 指向了 D, 标记继续进行,这样 D 就不会变成黑色, 因为 A 已经被标记了黑色,就不会再处理了。这样就产生了 D(白色) 被回收了。程序就错误了,感觉上就出现了悬挂指针

A (黑) -> B (灰) -> C (白) 
  ↓
D (白)

欢迎关注:程序员财富自由之路