【面试八股总结】GC垃圾回收机制

        垃圾回收策略(Garbage Collection,GC)用于回收不再使用的内存,避免系统的内存被占满。Go1.3之前采用标记清除法, Go1.3之后采用三色标记法,Go1.8采用三色标记法+混合写屏障。

前置概念:

1)根对象:应用程序中可以直接或间接访问的对象,主要包括:

  • 全局变量:在程序编译期间就能确定,全局变量存在于程序的整个生命周期
  • 执行栈:Go语言中协程是分配在堆上,每个协程都含有自己的执行栈
  • 寄存器:寄存器的值可能表示一个指针,这些指针可能指向某些赋值器分配的堆内存区

2)STW(Stop-The-World):系统在执行特定操作时,暂停所有应用程序线程,直到某个特定的事件发生或处理完成。

1. 标记清除法

具体步骤:

1)首先从根对象出发,标记所有可达对象。

        这一过程通常使用深度优先搜索或广度优先搜索进⾏。标记的方式通常是将对象的标记位从未标记改为已标记。

2)在清扫阶段,遍历整个堆内存,将未被标记的对象视为垃圾,即不再被引用。所有未被标记的对象都将被回收,它们的内存将被释放,以便后续的内存分配。

        标记清除算法执行完清扫阶段后,可能会产生内存碎片,即一些被回收的内存空间可能是不连续的。为了解决这个问题,一些实现中可能会进行内存碎片整理。

总结:

        标记清除算法的主要优势是能够回收不再使用的内存,但它也有一些缺点,其中主要的缺点是清扫阶段可能会引起一定程度的停顿,因为在这个阶段需要遍历整个堆内存。另外,由于标记清除算法只关注“存活”和 “垃圾”两种状态,不涉及内存分配的具体位置,可能导致内存碎片的产生

2. 三色标记法

        三色标记是指将对象分为三种颜色:白色、灰色、和黑色。

(1)白色:代表最终需要清理的对象内存块

(2)灰色:待处理的内存块

(3)黑色:活跃的内存块

具体步骤:

1)初始时,所有对象都被标记为白色,表示它们都是未被访问的垃圾对象。

2)根搜索:从根对象开始搜索,所有根对象被标记为灰色,表示它们是待处理的对象。

3)标记阶段:从灰色对象开始,垃圾回收器遍历对象的引用关系,将其引用的对象标记为灰色,然后当前该对象标记为黑色。重复这一步骤,直到所有可达对象都被标记为黑色。

4)清扫阶段:在标记完成后,垃圾回收器会扫描堆中的所有对象,将未被标记的对象回收(释放其内存)。这些未被标记的对象被认为是不可达的垃圾。

5)内存返还:垃圾回收完成后,系统中的内存得以回收并用于新的对象分配。

总结:

        1)优点:在标记阶段,垃圾回收器采用并发标记的方式,与程序的执行同时进行。这意味着程序的执行不会因为垃圾回收停顿,减小了对程序性能的影响。

        2)缺点:如果对象的引用被用户修改了,那么之前的标记就⽆效了。        

        3)垃圾回收的触发条件通常是在分配新对象时,如果达到⼀定的内存分配阈值,就会触发垃圾回收。 另外,⼀些特定的事件(如系统调用、网络阻塞等)也可能触发垃圾回收。       

3. 三色标记法+混合写屏障

        屏障技术可以理解为一种回调机制,在程序的某种执行过程中加一个判断机制,满足判断机制则执行回调函数,类似于钩子函数(Hook)。

        实际上,垃圾回收机制只用于回收堆上的内存,栈中的内存如局部变量、函数调用等会在调用结束后自动释放。也就是说,屏障机制只能作用于堆对象。屏障机制分为插入写屏障和删除写屏障:

  • 插入写屏障实现了强三色不变性,给对象添加引用关系时触发
  • 删除写屏障实现了弱三色不变性,删除对象引用关系时触发
  • 强三色不变性强制性的不允许黑色对象引用白色对象;
  • 弱三色不变性允许黑色对象引用白色对象,但是白色对象必须存在其他灰色对象对它的引用。

插入写屏障:每当一个对象被引用,就会触发判断:如果这次操作是一个白色对象被黑色对象引用,就把这个白色对象标记为灰色

删除写屏障:每当一个对象被删除时,就会触发判断:如果是一个灰色对象引用的白色对象被删除,那么就把这个白色对象标记为灰色

插入写屏障和删除写屏障都有各自的短板:

  • 插入写屏障结束时需要 STW(标记清除)重新扫描栈

  • 删除写屏障回收精度低,在开始时需要 STW 扫描整个堆栈记录初始快照

        基于插入写屏障和删除写屏障在结束时需要STW来重新扫描栈,带来性能瓶颈。混合写屏障分为以下四步:

  1. GC开始时,将栈上的全部对象标记为黑色(不需要二次扫描,无需STW);

  2. GC期间,任何栈上创建的新对象均为黑色 

  3. 被删除引用的对象标记为灰色 

  4. 被添加引用的对象标记为灰色

        因此,一次完整的GC分为四个阶段:

  1. 准备标记(需要STW),开启写屏障。
  2. 开始标记
  3. 标记结束(STW),关闭写屏障
  4. 清理(并发)

总结:

        标记清除法需要一次全局 STW,先标记可达对象,再清除所有的不可达对象,性能很差;

        三色标记法身无法在并发条件下正确执行,单独使用也需要 STW 才能保证正确性;

        为了优化这一过程,避免全局 SWT,引入了插入写屏障,插入写屏障是一种触发机制,每当一个对象被引用时,如果是黑色对象引用白色对象,就把这个白色对象置灰,但是由于屏障机制无法作用到栈对象,为了避免与栈相关的活跃对象被错误回收,需要在三色标记法结束后,对栈做一次 STW,清除掉垃圾对象;插入写屏障虽然优化了全局 SWT,但是每次GC也需要对栈上的一次 STW;

        为了解决这一问题,最终的方案是混合写屏障,混合写屏障的触发机制有两个:被删除的对象会被标记为灰色,被添加的对象会被标记为灰色;同时在开始时会把栈上所有可达对象置黑,栈上对象被创建时也会置黑。这样就记录了栈上的对象状态,避免了错误回收活跃对象;

        混合写屏障结合了插入写屏障和删除写屏障的优点,避免了在插入写屏障结束后需要进行全栈扫描的性能问题,实现了更高效的垃圾回收机制。

参考:代码随想录、Go语言的垃圾回收机制,图文并茂 一篇搞懂!_go垃圾回收-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值