面试题Go语言之垃圾回收机制

垃圾回收(Garbage Collection,简称GC)是编程语言中提供的自动的内存管理机制,自动释放不需要的对象,让出存储器资源,无需程序员手动执行。

​ Golang中的垃圾回收主要应用三色标记法,GC过程和其他用户goroutine可并发运行,但需要一定时间的STW(stop the world),STW的过程中,CPU不执行用户代码,全部用于垃圾回收,这个过程的影响很大,Golang进行了多次的迭代优化来解决这个问题。

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

此算法主要有两个主要的步骤:标记(Mark phase)和清除(Sweep phase)。第一步,暂停程序业务逻辑, 找出不可达的对象,然后做上标记。第二步,回收标记好的对象。操作非常简单,但是有一点需要额外注意:mark and sweep算法在执行的时候,需要程序暂停!即 STW(stop the world)。也就是说,这段时间程序会卡在哪儿。
在这里插入图片描述

第二步, 开始标记,程序找出它所有可达的对象,并做上标记。如下图所示:
在这里插入图片描述

第三步, 标记完了之后,然后开始清除未标记的对象. 结果如下:
在这里插入图片描述

第四步, 停止暂停,让程序继续跑。然后循环重复这个过程,直到process程序生命周期结束。

mark and sweep算法优缺点很明显,首先是STW,stop the world;让程序暂停,程序出现卡顿 (重要问题);其次是标记需要扫描整个heap;最后是清除数据会产生heap碎片。

所以Go V1.3版本之前就是以上来实施的, 流程大致是【启动STW】→【Mark标记】→【Sweep清除】→【停止STW】。同时这也是Stop The World的暂停范围(好像JOJO里面Dio和承太郎的替身能力)
在这里插入图片描述
我们都知道的事情Go开发者自然也清楚,随其Go V1.3 做了简单的优化,将STW提前, 减少STW暂停的时间范围。如下所示:
在这里插入图片描述
但是这里面最重要的问题就是:mark-and-sweep 算法会暂停整个程序 。这个缺点并没有得到解决。Go是如何面对这个问题的呢?接下来G V1.5版本就用三色并发标记法来优化了这个问题。

Go V1.5的三色并发标记法

三色标记法 实际上就是通过三个阶段的标记来确定清楚的对象都有哪些。我们来看一下具体的过程。

第一步 , 就是只要是新创建的对象,默认的颜色都是标记为“白色”.
在这里插入图片描述
这里面需要注意的是, 所谓“程序”, 则是一些对象的跟节点集合.
在这里插入图片描述
所以上图,可以转换如下的方式来表示.

第二步, 每次GC回收开始, 然后从根节点开始遍历所有对象,把遍历到的对象从白色集合放入“灰色”集合。
在这里插入图片描述
第三步, 遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
在这里插入图片描述
第四步, 重复第三步, 直到灰色中无任何对象.
在这里插入图片描述
第五步: 回收所有的白色标记表的对象. 也就是回收垃圾.
在这里插入图片描述
以上便是三色并发标记法, 不难看出,我们上面已经清楚的体现三色的特性, 那么又是如何实现并行的呢?

我们还是基于上述的三色并发标记法来说, 他是一定要依赖STW的. 因为如果不暂停程序, 程序的逻辑改变对象引用关系, 这种动作如果在标记阶段做了修改,会影响标记结果的正确性。我们举一个场景.

如果三色标记法, 标记过程不使用STW将会发生什么事情?
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

可以看出,有两个问题, 在三色标记法中,是不希望被发生的

  • 条件1: 一个白色对象被黑色对象引用(白色被挂在黑色下)
  • 条件2: 灰色对象与它之间的可达关系的白色对象遭到破坏(灰色同时丢了该白色)

当以上两个条件同时满足时, 就会出现对象丢失现象!

当然, 如果上述中的白色对象3, 如果他还有很多下游对象的话, 也会一并都清理掉.

为了防止这种现象的发生,最简单的方式就是STW,直接禁止掉其他用户程序对对象引用关系的干扰,但是STW的过程有明显的资源浪费,对所有的用户程序都有很大影响,如何能在保证对象不丢失的情况下合理的尽可能的提高GC效率,减少STW时间呢?​ 答案就是, 那么我们只要使用一个机制,来破坏上面的两个条件就可以了.

屏障机制

我们让GC回收器,满足下面两种情况之一时,可保对象不丢失. 所以引出两种方式.

(1) “强-弱” 三色不变式
  • 强三色不变式

不存在黑色对象引用到白色对象的指针。

在这里插入图片描述

弱三色不变式
所有被黑色对象引用的白色对象都处于灰色保护状态.
在这里插入图片描述
为了遵循上述的两个方式,Golang团队初步得到了如下具体的两种屏障方式“插入屏障”, “删除屏障”.

插入屏障

具体操作: 在A对象引用B对象的时候,B对象被标记为灰色。(将B挂在A下游,B必须被标记为灰色)

满足: 强三色不变式. (不存在黑色对象引用白色对象的情况了, 因为白色会强制变成灰色)

// 伪码
添加下游对象(当前下游对象slot, 新下游对象ptr) {   
  //1
  标记灰色(新下游对象ptr)   
  
  //2
  当前下游对象slot = 新下游对象ptr                    
}

场景:

A.添加下游对象(nil, B)   //A 之前没有下游, 新添加一个下游对象B, B被标记为灰色
A.添加下游对象(C, B)     //A 将下游对象C 更换为B,  B被标记为灰色

​这段伪码逻辑就是写屏障,. 我们知道,黑色对象的内存槽有两种位置, 栈和堆. 栈空间的特点是容量小,但是要求相应速度快,因为函数调用弹出频繁使用, 所以“插入屏障”机制,在栈空间的对象操作中不使用. 而仅仅使用在堆空间对象的操作中.

接下来,我们用几张图,来模拟整个一个详细的过程, 希望您能够更可观的看清晰整体流程。
在这里插入图片描述
在这里插入图片描述

未完待续…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JIeJaitt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值