自动内存管理有3个主要的核心任务:
- 为新对象分配空间
- 找到存活对象
- 回收死亡对象的内存空间
GC 工作机制
两种常见的GC相关的技术:
- 追踪垃圾回收(Tracing garbage collection)
- 引用计数(Reference counting)
追踪垃圾回收算法垃圾回收步骤:
-
标记根对象
- 标记包括 静态变量、全局变量、常量、线程栈等
-
标记:找到可达对象
- 求指针指向关系的传递闭包:从根对象触发,找到所有可达对象
-
清理:所有不可达对象
- 将存活对象复制到另外的内存空间(
Copying GC) - 将死亡对象的内存标记为”可分配“(
Marking-sweep GC) - 移动并整理存活对象(
Mark-compact GC)
- 将存活对象复制到另外的内存空间(
清理策略有很多种,在实际清理的时候应该根据对象的生命周期,使用不同的标记和清理策略。
标记-清扫算法 (mark-sweep)
标记清除算法是最常见的垃圾收集算法,标记清除收集器是跟踪式垃圾收集器,其执行过程可以分成标记(Mark)和清除(Sweep),算法流程如下:
- 标记阶段:暂停应用程序的执行,从根对象触发查找并标记堆中所有存活的对象;
- 清除阶段:遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表,恢复应用程序的执行;
三色标记算法
原始标记清除算法带来的长时间STW, 为了解决这一问题,Go从V1.5版本实现了基于三色标记清除的并发垃圾收集算法,在不暂停程序的情况下即可完成对象的可达性分析,三色标记算法将程序中的对象分成白色、黑色和灰色三类,算法流程如下:
- 遍历根对象的第一层可达对象标记为灰色, 不可达默认白色。
- 将灰色对象的下一层可达对象标记为灰色, 自身标记为黑色。
- 多次重复步骤2, 直到灰色对象为0, 只剩下白色对象和黑色对象。
- 回收白色对象。
混合写屏障
插入写屏障和删除写屏障的虽然大大的缩短的系统 GC 的 STW 时间,但是也有其短板:
- 插入写屏障:结束时需要 STW 来重新扫描栈,标记栈上引用的白色对象的存活;
- 删除写屏障:回收精度低, GC 开始时 STW 扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。
Go V1.8 版本引入了混合写屏障机制(hybrid write barrier),避免了对栈re-scan的过程,极大的减少了STW的时间。
具体操作:
- GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW),
- GC期间,任何在栈上创建的新对象,均为黑色。
- 被删除的对象标记为灰色。
- 被添加的对象标记为灰色。