原文地址:Go面试看这里了~(二十九)
1、常见垃圾回收机制?
常见垃圾回收机制一:引用计数。
意思是对每个对象维护一个引用计数,当引用对象的对象被销毁时,引用计数减去1,如引用计数为0则进行垃圾回收,此方式的优点是对象可以很快被回收,不会出现内存耗尽或达到阀值时才回收的情况,缺点是不能很好的处理循环引用,且实时维护引用计数也有一定的代价,在Python、PHP、Swift中有使用。
常见垃圾回收机制二:标记-清除。
从根变量开始遍历所有引用的对象,引用的对象标记为【被引用】,没有被标记的进行回收,优点是解决了引用计数的缺点,缺点是需STW,也就是要暂时停掉程序运行,例子就是Golang采用的三色标记法。
常见垃圾回收机制三:分代收集。
按对象生命周期长短划分不同的代空间,生命周期长的放入老年代,短的放入新生代,不同代有不同的回收算法和回收频率,优点是回收性能好,缺点是算法复杂,JAVA中有体现。
2、GC垃圾回收机制?
Go通过gcmarkBits位图标记span的块是否被引用,对应内存分配中的bitmap区,来看图片感受下:
三色标记含义:
-
灰色:对象已被标记,但该对象包含的子对象未标记。
-
黑色:对象已被标记,且该对象包含的子对象也已标记,gcmarkBits对应的位为1(该对象不会在本次GC中被清理)。
-
白色:对象未被标记,gcmarkBits对应的位为0(该对象本次GC将被清理)。
举个栗子,当前内存中有A~F一共6个对象,根对象a、b本身为栈上分配的局部变量,根对象a、b分别引用了对象A、B, 而B对象又引用了对象D,则GC开始前各对象的状态如下图所示:
可看到初始状态下所有对象都是白色的,接着开始扫描根对象a、b,由于根对象引用了对象A、B,那么A、B变为灰色对象,接下来就开始分析灰色对象,分析A时,A没有引用其它对象,所以很快就转入黑色,B引用了D,则B转入黑色的同时还需要将D转为灰色,接下来灰色对象只有D,由于D没有引用其它对象,所以D转入黑色,标记过程到此结束,最后黑色的对象会被保留下来,白色对象会被回收掉。
3、GC触发的方式?
-
阈值:默认内存扩大一倍,启动gc。
-
定期:默认2min触发一次gc,src/runtime/proc.go:forcegcperiod。
-
手动:runtime.gc()。
4、STW(stop the world)?
是GC最大的性能问题,对于GC而言,需要停止所有内存变化,也就是停止所有goroutine,等待gc结束之后才恢复,标记-清除(mark and sweep)算法的STW(stop the world)操作,就是runtime把所有线程全部冻结,意味着此时用户逻辑是暂停的,如此所有的对象都不会被修改,此时去扫描是绝对安全的。
标记-清除(mark and sweep)算法包含两部分逻辑,标记和清除,Go三色标记法中最后只剩下的黑白两种对象,黑色对象是程序恢复后接着使用的对象,如不碰黑色对象,只清除白色对象,就不会影响程序逻辑,所以清除操作和用户逻辑可以并发,标记操作和用户逻辑也是并发的。
5、GC操作流程?
-
Sweep Termination:对未清扫的span进行清扫,只有上一轮GC清扫工作完成才可开始新一轮的GC。
-
Mark:扫描所有根对象及根对象可以到达的所有对象,标记其不被回收。
-
Mark Termination:完成标记工作,重新扫描部分根对象(要求STW)。
-
Sweep:按标记结果清扫span。
整个GC流程会进行两次STW(Stop The World),第一次是Mark阶段的开始,此时STW会准备根对象的扫描,启动写屏障(Write Barrier)和辅助GC(mutator assist),第二次是Mark Termination阶段,此时STW会重新扫描部分根对象,禁用写屏障(Write Barrier)和辅助GC(mutator assist)。
要注意的事,不是所有根对象的扫描都需要STW,如扫描栈上的对象只需停止拥有该栈的G,从go 1.9开始,写屏障的实现使用了Hybrid Write Barrier,大幅减少了第二次STW的时间。
6、并行GC失败操作流程?
因为Go支持并行GC,也就是支持GC扫描和Go程序同时运行,带来的问题是GC描的过程中Go程序可能改变对象的依赖树,如GC扫描时发现根对象A和B,B拥有C的指针,此时GC先扫描A,A无对其它对象的引用,所以GC将A放入黑色,之后Go程序操作B将C的指针交给A,再往后GC扫描B时,B无对其它对象的引用,所以GC将B放入黑色,但现在C在白色,所以会被回收,但A确实引用C。
7、GC写屏障(Write Barrier)?
Go在GC标记阶段会启用写屏障,启用后操作流程如下:
-
GC先扫描A,A放入黑色。
-
B把C的指针交给A。
-
由于A在黑色,所以C放入灰色。
-
C没有子对象,放入黑色。
-
扫描B,B没有子对象,放入黑色。
即使A在稍后丢掉C,下一轮GC也会回收C,开启写屏障后,当指针发生改变,GC会认为在这一轮扫描中该指针是存活的,所以会放入灰色。
至此,本次分享就结束了,后期会慢慢补充。
以上仅为个人观点,不一定准确,能帮到各位那是最好的。
好啦,到这里本文就结束了,喜欢的话就来个三连击吧。
扫码关注公众号,获取更多优质内容。