GC

本文最后更新于:2021年9月20日 上午

这里只是简单介绍一下GC,我本人对于GC的了解并不是特别深入

GC

垃圾回收算法

首先我们来介绍一下GC算法的大致思路。

  1. 引用计数:每个对象维护一个域,其中保存其它对象指向它的引用数量,当引用数量为零时该对象为垃圾
  2. 标记-清除:首先使用标记算法标记一些对象为不可达对象,之后在达到某个阈值或者固定时间的话,这个时候系统会挂起用户程序,也就是SWT,转而执行垃圾回收算法
  3. 节点复制
  4. 分代收集:分代垃圾回收算法将对象按生命周期长短存放到堆上的两个(或者更多)区域,这些区域就是分代(generation)。之后再进行垃圾回收

这里 Golang 使用的是基于 标记-清除 法的三色标记算法

GC触发条件

一般来说有三种情况会触发GC

  1. 辅助GC,在分配内存时,会判断当前的Heap内存分配量是否达到了触发GC的阈值,如果超过了阈值则启动一轮GC
  2. 调用 systeme.GC() 来强制启动一轮GC
  3. 当超过一定时间未运行GC的情况下会执行GC

为什么需要三色标记算法

相比之前的标记清除算法,其GC执行期间需要把整个程序完全暂停,不能异步执行GC操作。对实时性要求比较高的系统来说,这种需要长时间挂起的标记清除算法是不可接受的,而三色标记算法就很好的解决了这个问题。 三色标记最大的好处是可以异步执行,从而可以以中断时间极少的代价或者完全没有中断操作来进行整个GC。

三色指的是黑色、灰色、白色,

  1. 黑色:根对象,或者某对象和它的子对象都被扫描过
  2. 灰色:某对象被扫描过,但是子对象未被扫描
  3. 白色:未被扫描的对象,如果扫描完成所有对象之后,最终白色为不可达对象,即垃圾对象

读写屏障

所谓的读写屏障就是针对某个对象变量赋值、读取时做一些格外的操作

垃圾回收的主要流程

  1. 初始化所有对象被标记为白色
  2. 从root开始找到所有可达对象,标记为灰色,放入到待处理队列中
  3. 遍历灰色对象队列,把其引用的对象标记为灰色放入到待处理队列中,自身标记为黑色
  4. 处理完灰色对象队列,知道没有灰色对象为止
  5. 其余白色为垃圾对象,执行清扫工作

三色标记法的引用消失问题

什么是引用消失:即原本应该是黑色的对象被误标为白色。

发生的条件

当且仅当一下两个条件同时满足的时候

  1. 赋值器插入了一条或多条从黑色对象到白色对象的新引用
  2. 赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。

解决办法

只需要破坏成立的条件即可,有两种方案:增量更新和原始快照

  1. 增量更新,当一个白色对象被黑色对象引用时,把黑色对象重新标记为灰色,让垃圾回收算法重新扫描即可
  2. 原始快照:原始快照要破坏的是第二个条件,当灰色对象要删除白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再扫描这些灰色节点

对比

原始快照关注的是引用删除,增量更新关注的是引用增加

因为增量更新算法还需要把原本为黑色的节点再次扫描一遍,效率有点低下

写在最后

2021-07-13 14:59

哈哈哈,今天并没有日志


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!