1. go语言垃圾回收方式的本质
常用的垃圾回收方式主要有
- 引用计数 - 对象引用计数等于0的回收。有互相引用计数等各种问题。实际应用很少。
- 标记清除 - 在回收期把标记存活外的对象内存块清理。存在内存碎片问题
- 标记复制 - 在回收期把标记存活的复制到一个全新的区域。避免了碎片问题,但需要应用2倍的存储空间。
go的垃圾回收本质上是标记清除
2. go标记清除与内存模型
和Java的分代收集不同。go的内存GC配合内存管理模型(TCMalloc
),使用三色标记法
进行收集。
- TCMalloc:偏向用户态以及减少碎片化(在之前的文章已描述)
- 三色标记法: 减少stop the world时间
三色标记法原理:
- 所有对象默认白色
- 标记 GC ROOT(全局变量、执行栈、寄存器) 能关联到的对象灰色。
- 遍历灰色对象的子节点,标记灰色对象为黑色,子节点为灰色。
- 依次递归遍历到没有子节点为止。
- 最终所有可达节点全部为黑色。
三色标记法示意图
示意图假设 a, g对象为gc root对象。标记结束会回收 f 节点占用的内存块。
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 | 内存归还阶段,将需要回收的内存归还给操作系统,写屏障关闭 | 并发 |