关于Golang GC的一些误解,真的比Java算法更领先吗?
首先强调下本文的起因是在高可用架构后花园群的一次聊天,大家在争论Golang的GC到底是类似Java的ZGC还是类似Java的CMS GC。我个人的看法是Golang的GC是类似于Java的CMS GC,官方的mgc的注释这么说的:
其中mutator是指我们的应用程序,因为可能会改变内存的状态,所以命名为mutator。这段话翻译过来,大概的意思就是说Go的GC使用的是一种非分代的没有整理过程的Concurrent Mark and Sweep算法(CMS算法),我个人再补充下,标记过程(即Mark过程)是使用三色标记法。讨论的过程中出现两个错误观点,一个是CMS算法一定是分代的,另一个是使用了三色标记法就是类似于ZGC的做法(我个人不知道为啥有这个观点,当时也忘记问清楚了,可能是把三色标记法和ZGC里的指针染色搞混了)。至于CMS是否一定要分代,我给一篇介绍,再借用R大的一句话给问题先做个结论,“只要不移动对象做并发GC,最终就会得到某种形式的CMS。” 标记-清理算法 标记-清理算法是一种追踪式的垃圾回收算法,并不会在对象死亡后立即将其清理掉,而是在一定条件下触发,统一校验系统中的存活对象,进行回收工作。 标记-清理分为两个部分,标记和清理,标记过程会遍历所有对象,查找出死亡对象。通过GC ROOT到对象的可达性就可以确认对象的存活,也就是说,如果存在一条从GC ROOT出发的引用最终可指向某个对象,就认为这个对象是存活的。这样,未能证明存活的对象就可以标记为死亡了。标记结束后,再次进行遍历,清理掉确认死亡的对象。 标记清理都是并发执行的标记-清理算法就是CMS。三色标记法是一种标记对象使用的算法。 Go GC的改进历史
GC 流程 1.5
GC 1.8 1.8引入混合屏障,最小化第一次STW,混合屏障是指: 写入屏障,在写入指针f时将C对象标记为灰色。Go1.5版本使用的Dijkstra写屏障就是这个原理,伪代码如下:
删除屏障,使用的Yuasa屏障伪代码如下:
1.8中引入的混合屏障,写入屏障和删除屏障各有优缺点,Dijkstra写入写屏障在标记开始时无需STW,可直接开始,并发进行,但结束时需要STW来重新扫描栈,标记栈上引用的白色对象的存活;Yuasa删除屏障则需要在GC开始时STW扫描堆栈来记录初始快照,这个过程会记录开始时刻的所有存活对象,但结束时无需STW。Go1.8版本引入的混合写屏障结合了Yuasa的删除写屏障和Dijkstra的写入写屏障,结合了两者的优点,伪代码如下:
因此,个人的理解是在Mark init阶段开始的时候激活混合写屏障这时候STW,在rescan阶段应该也只需要在去掉混合写屏障的时候STW。从算法上来看,是接近Java CMS算法,而非ZGC,当然Go GC的比Java CMS GC有很多实现上的优化。 为了了解其细节,查到William有篇文章讲了不少GC细节,译文如下。 在Go 1.12版本里,Go垃圾收集器依然使用非分代的并发的三色标记清理算法。Ken Fox这篇文章里关于GC的动画非常赞。Go的垃圾收集器的实现随着Go版本的变化而发生变化。因此,一旦发布下一版本,很多细节可能会有不同。 垃圾收集器行为 (编辑:ASP站长网) |