原文地址:Go面试看这里了~(八)
1、插入写屏障?
Go回收没有混合屏障前,一直是插入写屏障,由于栈赋值没有hook的原因,所以栈没启用写屏障,所以有STW。
Go解决方案是只在需要结束时启用STW来重新扫描栈,此操作会导致整个进程的赋值器卡顿,所以后面Go引用混合写屏障解决此问题,之后就没有STW。
2、删除写屏障?
Go没有此操作,Go内存写屏障是由插入写屏障到混合写屏障过度的,对象即使被删除,最后一个指向它的指针依旧可活过这一轮,在下一轮被gc清理掉。
3、写屏障?
Go进行三色标记时并没有STW,此时对象还可修改,来看如下情况:
在进行三色标记中扫描灰色集合,扫描到对象A,并标记A的所有引用,此时开始扫描对象D的引用,同时,另一goroutine修改了D->E的引用,如下:
如此就不会导致E扫描不到,而被误认为白色对象,写屏障就是为了解决此问题,引入写屏障之后,E被认为是存活的,即使后面E被A抛弃,E在这一轮不会被gc回收,而在下一轮被gc回收。
4、混合写屏障?
-
混合写屏障继承插入写屏障优点,起始无需STW打快照,直接并发扫描垃圾。
-
混合写屏障继承删除写屏障优点,赋值器是黑色赋值器,gc期间,任何在栈上创建新的对象均为黑色,扫描一次之后就不再扫描了,如此就消除了插入写屏障时最后STW的重新扫描栈。
-
混合写屏障扫描精度与删除写屏障一致,比插入写屏障低,随着带来的是gc过程全程无STW。
-
混合写屏障扫描虽无STW,但扫描具体的栈时,还是需停止此goroutine赋值器的工作(针对goroutine栈来说,是暂停扫描的,要么全黑,要么全灰,状态切换是原子操作)。
5、gc触发时机?
主动触发:用户代码调用runtime.GC。
-
被动触发:使用sysmon,触发条件由runtime.forcegcperiod变量控制,默认两分钟,当超过2分钟没进行任何gc时,强制触发gc。
-
使用步调(Pacing)算法,其核心思想是控制内存增长的比例,如Go的gc是一种比例gc,下次gc结束时的堆大小和上次gc存活堆大小成比例。
-
由GOGC控制,默认100,也就是2倍的关系,200也就是3倍的关系。
-
Go创建新对象所占内存大小除以上次gc结束后保留下来的对象占用内存大小。
6、gc的流程?
以STW为界限分为五个阶段:
-
说明赋值器状态GCMark标记准备阶段。
-
为并发标记做准备工作,启动写屏障STWGCMark扫描标记阶段。
-
与赋值器并发执行,写屏障开启并发GCMarkTermination标记终止阶段。
-
保证一周期内标记任务完成,停止写屏障STWGCoff内存清扫阶段。
-
将需要回收的内存归还到堆中,写屏障关闭并发GCoff内存归还阶段,将过多的内存归还给操作系统,写屏障关闭并发。
7、gc如何调优?
可通过go tool pprof和go tool trace等工具,如下:
-
控制内存分配速度,限制goroutine数量,提高赋值器对CPU的利用率。
-
减少并复用内存,用sync.Pool复用需频繁创建的临时对象,或是提前分配足够的内存来降低多余的拷贝。
-
需要时增大GOGC的值,降低GC运行频率。
至此,本次分享就结束了,后期会慢慢补充。
以上仅为个人观点,不一定准确,能帮到各位那是最好的。
好啦,到这里本文就结束了,喜欢的话就来个三连击吧。
扫码关注公众号,获取更多优质内容。



