这是我参与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 过程如下
最终回收的是白色对象
- GC. Root 根对象标记为灰色
- 然后灰色集合中获取对象,将其标记为黑色,将该对象引用到的对象标记为灰色
- 重复步骤2, 知道没有灰色集合可以标记为止
- 结束后,剩下的没有标记的白色对象即为
GC Roots不可达,可以进行回收
三色标记法存在的问题
多标问题-浮动垃圾
假设标记过程中出现如下:
D(黑色)-> E(灰色)-> F(白色)
↓
G(白)
假设标记过程中,应用程序断开了 D 对 E 的引用 , 按理论来说 E/F/G 都应该被回收,但是实际上 E 已经被标记为灰色, 将会被当作存活对象继续遍历下去,从灰色变成黑色,F/G 标记为灰色,导致这部分对象将继续存活下去。 这个就感觉产生了 浮动垃圾 问题了。
漏标问题 -悬挂指针
假设标记过程中出现如下:
A(黑)-> B(灰)-> C(白) -> D(白)
正常情况下,D 存在引用,会被标记成黑色, 但是在并行标记过程中,程序也在并发执行。假设用户程序删除了 C 对 D 的引用, 并且使得。A 指向了 D, 标记继续进行,这样 D 就不会变成黑色, 因为 A 已经被标记了黑色,就不会再处理了。这样就产生了 D(白色) 被回收了。程序就错误了,感觉上就出现了悬挂指针
A (黑) -> B (灰) -> C (白)
↓
D (白)