1. go语言垃圾回收方式的本质

常用的垃圾回收方式主要有

  • 引用计数 - 对象引用计数等于0的回收。有互相引用计数等各种问题。实际应用很少。
  • 标记清除 - 在回收期把标记存活外的对象内存块清理。存在内存碎片问题
  • 标记复制 - 在回收期把标记存活的复制到一个全新的区域。避免了碎片问题,但需要应用2倍的存储空间。

go的垃圾回收本质上是标记清除

2. go标记清除与内存模型

和Java的分代收集不同。go的内存GC配合内存管理模型(TCMalloc),使用三色标记法进行收集。

  • TCMalloc:偏向用户态以及减少碎片化(在之前的文章已描述)
  • 三色标记法: 减少stop the world时间

三色标记法原理

  • 所有对象默认白色
  • 标记 GC ROOT(全局变量、执行栈、寄存器) 能关联到的对象灰色。
  • 遍历灰色对象的子节点,标记灰色对象为黑色,子节点为灰色。
  • 依次递归遍历到没有子节点为止。
  • 最终所有可达节点全部为黑色。

三色标记法示意图

示意图假设 a, g对象为gc root对象。标记结束会回收 f 节点占用的内存块。

#674px #384px

3. 怎么做到并行收集

3.1 并行过程中悬空指针问题的解决

打开写屏障的过程需要 stop the world

为了最大限度的减少stw(stop the world)。GC算法优化尽量在内存标记阶段减少stw的操作,使标记和用户线程能并行进行。同时进行会出现某些对象指针变动过程中悬空(脱离原有对象,还没赋值到新对象的过程中标记结束),没有被标记成为悬空指针被错误回收。

为了实现并行标记过程中不出现悬空指针,一般的做法是加入写屏障技术。写屏障指令使受写屏障保护的指令段顺序执行,同时对标记过程中出现的对象赋值进行shade(标记为灰色)操作。此方法可能会标记冗余的对象,但解决了悬空指针问题

3.2 并行过程中垃圾产生过快问题解决

并行过程中如果用户线程产生增量内存垃圾过快,则需要有专门线程抑制用户线程过快地产生垃圾。此过程叫做辅助GC。最终效果类似stw。

4. 附录

GC各阶段说明

阶段 说明 赋值器状态
SweepTermination 清扫终止阶段,为下一阶段的并发标记做准备工作,启动写屏障 STW
Mark 扫描标记阶段,与赋值器并发执行,写屏障开启 并发
MarkTermination 标记终止阶段,保证一个周期内标记任务完成,停止写屏障 STW
GCoff 内存清扫阶段,将需要回收的内存归还到堆中,写屏障关闭 并发
GCoff 内存归还阶段,将需要回收的内存归还给操作系统,写屏障关闭 并发