Go内存管理——垃圾回收| 青训营笔记

183 阅读3分钟

自动内存管理有3个主要的核心任务:

  1. 为新对象分配空间
  2. 找到存活对象
  3. 回收死亡对象的内存空间

GC 工作机制

两种常见的GC相关的技术:

  • 追踪垃圾回收(Tracing garbage collection)
  • 引用计数(Reference counting)

追踪垃圾回收算法垃圾回收步骤:

  1. 标记根对象

    • 标记包括 静态变量、全局变量、常量、线程栈等
  2. 标记:找到可达对象

    • 求指针指向关系的传递闭包:从根对象触发,找到所有可达对象
  3. 清理:所有不可达对象

    • 将存活对象复制到另外的内存空间(Copying GC)
    • 将死亡对象的内存标记为”可分配“(Marking-sweep GC)
    • 移动并整理存活对象(Mark-compact GC)

清理策略有很多种,在实际清理的时候应该根据对象的生命周期,使用不同的标记和清理策略。

标记-清扫算法 (mark-sweep)

标记清除算法是最常见的垃圾收集算法,标记清除收集器是跟踪式垃圾收集器,其执行过程可以分成标记(Mark)和清除(Sweep),算法流程如下:

  1. 标记阶段:暂停应用程序的执行,从根对象触发查找并标记堆中所有存活的对象;
  2. 清除阶段:遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表,恢复应用程序的执行;

三色标记算法

原始标记清除算法带来的长时间STW, 为了解决这一问题,Go从V1.5版本实现了基于三色标记清除的并发垃圾收集算法,在不暂停程序的情况下即可完成对象的可达性分析,三色标记算法将程序中的对象分成白色、黑色和灰色三类,算法流程如下:

  1. 遍历根对象的第一层可达对象标记为灰色, 不可达默认白色。
  2. 将灰色对象的下一层可达对象标记为灰色, 自身标记为黑色。
  3. 多次重复步骤2, 直到灰色对象为0, 只剩下白色对象和黑色对象。
  4. 回收白色对象。

混合写屏障

插入写屏障和删除写屏障的虽然大大的缩短的系统 GC 的 STW 时间,但是也有其短板:

  1. 插入写屏障:结束时需要 STW 来重新扫描栈,标记栈上引用的白色对象的存活;
  2. 删除写屏障:回收精度低, GC 开始时 STW 扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。

Go V1.8 版本引入了混合写屏障机制(hybrid write barrier),避免了对栈re-scan的过程,极大的减少了STW的时间。

具体操作:

  1. GC开始将栈上的对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW),
  2. GC期间,任何在栈上创建的新对象,均为黑色。
  3. 被删除的对象标记为灰色。
  4. 被添加的对象标记为灰色。