这里只是简单介绍一下GC,我本人对于GC的了解并不是特别深入
GC
垃圾回收算法
首先我们来介绍一下GC算法的大致思路。
- 引用计数:每个对象维护一个域,其中保存其它对象指向它的引用数量,当引用数量为零时该对象为垃圾
- 标记-清除:首先使用标记算法标记一些对象为不可达对象,之后在达到某个阈值或者固定时间的话,这个时候系统会挂起用户程序,也就是SWT,转而执行垃圾回收算法
- 节点复制
- 分代收集:分代垃圾回收算法将对象按生命周期长短存放到堆上的两个(或者更多)区域,这些区域就是分代(generation)。之后再进行垃圾回收
这里 Golang
使用的是基于 标记-清除 法的三色标记算法
GC触发条件
一般来说有三种情况会触发GC
- 辅助GC,在分配内存时,会判断当前的Heap内存分配量是否达到了触发GC的阈值,如果超过了阈值则启动一轮GC
- 调用
systeme.GC()
来强制启动一轮GC - 当超过一定时间未运行GC的情况下会执行GC
为什么需要三色标记算法
相比之前的标记清除算法,其GC执行期间需要把整个程序完全暂停,不能异步执行GC操作。对实时性要求比较高的系统来说,这种需要长时间挂起的标记清除算法是不可接受的,而三色标记算法就很好的解决了这个问题。 三色标记最大的好处是可以异步执行,从而可以以中断时间极少的代价或者完全没有中断操作来进行整个GC。
三色指的是黑色、灰色、白色,
- 黑色:根对象,或者某对象和它的子对象都被扫描过
- 灰色:某对象被扫描过,但是子对象未被扫描
- 白色:未被扫描的对象,如果扫描完成所有对象之后,最终白色为不可达对象,即垃圾对象
读写屏障
所谓的读写屏障就是针对某个对象变量赋值、读取时做一些格外的操作
垃圾回收的主要流程
- 初始化所有对象被标记为白色
- 从root开始找到所有可达对象,标记为灰色,放入到待处理队列中
- 遍历灰色对象队列,把其引用的对象标记为灰色放入到待处理队列中,自身标记为黑色
- 处理完灰色对象队列,知道没有灰色对象为止
- 其余白色为垃圾对象,执行清扫工作
三色标记法的引用消失问题
什么是引用消失:即原本应该是黑色的对象被误标为白色。
发生的条件
当且仅当一下两个条件同时满足的时候
- 赋值器插入了一条或多条从黑色对象到白色对象的新引用
- 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。
解决办法
只需要破坏成立的条件即可,有两种方案:增量更新和原始快照
- 增量更新,当一个白色对象被黑色对象引用时,把黑色对象重新标记为灰色,让垃圾回收算法重新扫描即可
- 原始快照:原始快照要破坏的是第二个条件,当灰色对象要删除白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再扫描这些灰色节点
对比
原始快照关注的是引用删除,增量更新关注的是引用增加
因为增量更新算法还需要把原本为黑色的节点再次扫描一遍,效率有点低下
写在最后
2021-07-13 14:59
哈哈哈,今天并没有日志