GC垃圾回收

1. Go V1.3 之前标记清除法(mark and sweep)

1654606341761.png

程序可达对象有1->2->34->7等五个对象;

  1. 在进行垃圾回收时,为了避免程序错乱,首先会进行STW(stop the world),将程序业务逻辑进行暂停,然后将程序可达对象全部进行标记,未被标记的对象被视为垃圾需要进行回收。垃圾回收完成后,解除STW,程序继续执行;

1654608853544.png

1654609951534.png

标记清除法的GC垃圾回收 - 图4

  • 存在STW过程,会让程序暂停,程序会出现卡顿现象 (GC垃圾回收 - 图5);
  • 标记需要对整个heap扫描,耗时长;
  • 清除数据会产生heap碎片;

优化:

  1. 尽量缩短STW时间,将垃圾清除和停止STW进行互换,缩短STW时间,即先解除STW,然后执行垃圾回收(上面三、四步之间进行交换)。但是标记时间任然过长;

2. Go V1.5 三色标记法

1654606341761.png

程序可达对象有1->2->34->7等五个对象;

三色:GC垃圾回收 - 图7,分别对应三色标记表,其中灰色为临时状态,最终态为白色或者黑色;

  1. 初始将程序所有对象标记为白色,遍历一次程序根集合,将一次比那里可达对象标记为灰色,然后将可达对象由白色标记为灰色,并将对象移至灰色标记表;然后遍历灰色标记表,将可达对象由白色变为灰色,遍历之后的灰色变为黑色。重复之前操作,直到灰色标记表没有数据。**最后剩余的白色对象即为垃圾。**

1654613563582.png

1654613708690.png

GC垃圾回收 - 图10

  • 条件1:当一个白色对象被一个黑色对象引用(白色挂载在黑色下),如图对象3挂在对象4下;
  • 条件2:灰色对象与该白色对象之间可达关系被破坏(灰色丢了该白色),对象2丢失了对象3;

如果上述两个条件同时满足,就会GC垃圾回收 - 图11(会导致对象3丢失),所以GC垃圾回收 - 图12

1654614591902.png

解决办法:阻止上述两个条件同时成立

  • 强三色不变式:强制性的不允许黑色对象引用白色对象(破坏条件1)
  • 弱三色不变式:黑色可以引用白色,若白色对象被灰色对象(或白色对象链路上层存在灰色对象)引用(破坏条件2)

只要满足强/弱三色不变式之一,即可保证对象不丢失;

根据强/弱三色不变式,衍生了屏障机制:

  • 插入屏障(GC垃圾回收 - 图14,因为你每次引用对象都要进行判断,首先栈的空间较小,还需要提供日常函数调用,因此需要对性能有要求,因此插入屏障不再栈上使用)
    • 具体操作:在A对象引用B对象时,B对象被标记为灰色,满足强三色不变式(不存在黑色对象引用白色对象,因为白色对象全部变为灰色了)
      1654615439569.png
    • 缺点:因为栈上不使用插入屏障,所以如果栈上某个黑色对象引用了白色对象,最终白色对象会被丢弃 上面对象9会被丢失
    • 解决办法:回收白色对象之前,启动stw,将栈上数据全部变为白色,在进行一次扫描。停止stw,进行清除白色操作 就可以避免对象9丢失 这样stw比较短暂
      插入写屏障不足:结束时需要STW重新扫描栈空间;
      1654615562409.png
  • 删除屏障:还是栈上不使用
    • 具体操作:被删除对象,如果自身为灰色或者白色,那么被标记为灰色 满足弱三色不变式(保护灰色对象到白色对象的路径不会断)
    • 缺点:回收精度比较低,被删除的对象在下一轮才会被清理掉

3. Go V1.8 三色标记法+混合写屏障机制

栈上不使用:

  • GC将栈上可达对象全部置黑色(之后不再进行二次扫描,无需STW)
  • GC期间,在栈上创建新对象,都是黑色
  • 被删除对象置为灰色
  • 被添加对象置为灰色