【Go 原理】GC (下)混合写屏障机制

插入屏障 vs 删除屏障

什么是插入屏障?什么是删除屏障?他们的缺点是什么?

  • 插入屏障(强三色不变式)的原理:在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须被标记为灰色,不存在黑色对象引用白色对象的情况, 因为白色会强制变成灰色)。

  • 删除屏障(弱三色不变式)的原理:被删除的对象,如果自身为灰色或者白色,那么被标记为灰色。(被删除的对象,如果自身为灰色或者白色,那么被标记为灰色)。

  • 插入屏障的缺点:栈空间使用了STW扫描暂停,性能不佳。由于是在堆空间使用的插入屏障,而在栈空间还得使用STW来暂停进行三色标记扫描,结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活,这还是会导致性能的影响。STW大约的时间在10~100ms间。
  • 删除屏障的缺点 :这种方式的回收精度低。一个对象即使被删除了最后一个指向它的指针也依旧可以活过这一轮,在下一轮GC中被清理掉。

注:黑色对象的内存槽有两种位置,栈空间和堆空间。 栈空间的特点是容量小,但是要求相应速度快,因为函数调用弹出频繁使用,所以插入屏障机制在栈空间的对象操作中不使用,而仅仅使用在堆空间对象的操作中。但是如果栈不使用插入屏障机制,当全部三色标记扫描之后,栈上有可能依然存在白色对象被引用的情况, 所以要对栈重新进行三色标记扫描,但这次为了对象不丢失,要对本次标记扫描启动STW暂停,直到栈空间的三色标记结束。

插入屏障的整体流程(仅存堆空间):

  • ① 程序刚创建的对象标记为白色,将所有对象放入白色集合中。

    image-20211108102841534

  • ② 遍历根节点(非递归,只遍历一次),得到灰色对象。

    image-20211108102903833

  • ③ 遍历灰色标记表,将可达的对象,从白色标记为灰色,遍历之后的灰色标记为黑色。

    image-20211108102928950

  • ④ 由于并发特性,此刻外界向对象4添加对象8,对象1添加对象9。由于对象4位于堆空间,即将触发插入屏障,对象1在栈空间,不触发。

    image-20211108102949121

  • ⑤ 由于插入写屏障,在堆空间黑色对象添加白色,会将白色改为灰色。对象8改为灰色,对象9依然为灰色。

    image-20211108103013384

  • ⑥ 继续循环上述流程进行三色标记,直至没有灰色节点。

    image-20211108103042456

  • ⑦ 在准备回收白色前,重新扫描一次栈空间,此时加STW暂停保护栈,防止外界干扰(有新的白色被黑色添加)。

    image-20211108103104783

  • ⑧ 在STW中,将栈中的对象进行三色标记,直至没有灰色对象为止。

    image-20211108103119757

  • ⑨ 停止STW,清除白色对象。

    image-20211108103146107

删除屏障的整体流程(不区分栈和堆空间):

  • ① 程序刚创建的对象标记为白色,将所有对象放入白色集合中。

    image-20211108103443445

  • ② 遍历根节点(非递归,只遍历一次),得到灰色对象。

    image-20211108103503331

  • ③ 灰色对象1删除对象5,如果不触发删除屏障,5-2-3路径与主路径断开,最后均会被清除。

    image-20211108103529926

  • ④ 触发删除屏障,被删除的对象5,自身被标记为灰色。

    image-20211108103547056

  • ⑤ 遍历灰色标记表,将可达的对象白色标记为灰色,遍历之后的灰色标记为黑色。

    image-20211108103603822

  • ⑥ 继续循环上述流程进行三色标记,直至没有灰色节点。

    image-20211108103621905

  • ⑦ 清除白色对象。

image-20211108103635968

V1.8 混合写屏障机制

为什么要引入混合写屏障机制?具体原理是什么?列举几个场景验证该机制是否合理?

  • 引入混合写屏障机制是为了解决插入写屏障和删除写屏障的短板问题。
  • 插入写屏障短板:结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活,性能不佳。
  • 删除写屏障短板:回收精度低,GC开始时STW扫描堆栈来记录初始快照,这个过程会保护开始时刻的所有存活对象。
  • 具体操作(两黑两灰):① GC开始,优先扫描栈上的全部对象,将栈上可达对象标记为黑色。
  • GC期间,任何在栈上创建的新对象,均为黑色。
  • ③ 被删除的对象标记为灰色。
  • ④ 被添加的对象标记为灰色。
  • 场景一: 对象被一个堆对象删除引用,成为栈对象的下游
  • 场景二:对象被一个栈对象删除引用,成为另一个栈对象的下游
  • 场景三:对象被一个堆对象删除引用,成为另一个堆对象的下游
  • 场景四:对象从一个栈对象删除引用,成为另一个堆对象的下游

注:混合写屏障机制的本质是弱三色不变式,为了保证栈的运行效率,屏障技术是不在栈上使用的。混合写屏障是GC的一种屏障机制,所以只是当程序执行GC的时候,才会触发这种机制。

  • 场景一: 对象被一个堆对象删除引用,成为栈对象的下游。堆区,对象4删除对象7时,触发删除写屏障,将对象7置为灰色,对象7此时为灰色,处于被保护的状态。栈区不启动任何写屏障,所以直接将对象7挂在对象1下面。

image-20211108140824880

  • 场景二:对象被一个栈对象删除引用,成为另一个栈对象的下游。新创建一个对象9,因为混合写屏障模式中,GC过程中任何新创建的对象均标记为黑色。栈中不启动任何写屏障,对象9直接添加下游引用对象3,对象2直接删除对象3的引用关系。

image-20211108142005556

  • 场景三:对象被一个堆对象删除引用,成为另一个堆对象的下游。堆对象10添加下游引用堆对象7,触发屏障机制,被添加的对象7标记为灰色,对象6被保护。堆对象4删除堆对象7,触发屏障机制,被删除的对象7标记为灰色。

    image-20211108142544550

  • 场景四:对象从一个栈对象删除引用,成为另一个堆对象的下游。栈对象1删除栈对象2的引用,栈空间不触发写屏障;堆对象4删除堆对象7的引用关系,转移至栈对象2,堆对象4在删除的时候触发屏障,标记堆对象7为灰色,保护堆对象7及其下游节点对象。

    image-20211108143530162

    Golang中的混合写屏障满足弱三色不变式,结合了删除写屏障和插入写屏障的优点,只需要在开始时并发扫描各个goroutine的栈,使其变黑并一直保持,这个过程不需要STW,而标记结束后,因为栈在扫描后始终是黑色的,也无需再进行re-scan操作了,避免了对栈re-scan的过程,极大的减少了STW的时间。

相关推荐

微信扫一扫,分享到朋友圈

【Go 原理】GC (下)混合写屏障机制
返回顶部

显示

忘记密码?

显示

显示

获取验证码

Close