记杂||以java的角度来看go(GC篇)

本文类似于个人学习笔记不会面面俱到,需要读者掌握一定基本知识。

垃圾回收算法

垃圾标记算法:由于引用计数法的巨大缺陷,go和java都是直接或间接使用可达性分析算法。

垃圾回收算法:java以G1为例子,以分代GC为基础,通过分区机制、混合回收策略和智能内存管理优化。每一块(Eden、Survivor、Old或Humongous)都类似于标记-复制算法的From区和To区,这样既解决了传统标记复制算法空间利用率低的问题,同时还没有标记清除算法空间碎片化的缺点。同时G1可以设置最大STW时间,可见其先进和智能。

反观go使用的是三色标记法,标记清除算法的改进版。如果不清楚go为什么这么做,可能认为这是一种过时,缺陷大的方式。那我来讲讲:

1.为什么不使用分代GC算法?这要先说起垃圾回收,垃圾指什么?java垃圾回收的主要回收区域是堆,回收内容是堆上的对象。java的对象大部分都是在堆上分配内存的(少数进行逃逸分析和标量替换等),这样看来,在堆上分配的对象就有哪些经常用的,和不经常用的(分代GC以大部分死去对象都为新对象为前提)。这些对象先统一分配在伊甸园区,然后经常使用的对象“年龄”够大时送到老年代,不经常用的GC之后丢掉,这样既减少了GC压力,提高了吞吐等,看着非常合理。但是go哪?go也有逃逸分析,但go的逃逸分析是在编译时进行之后再决定分配在什么位置,java的只在jit编译优化时进行,效果差别巨大,这样go可以将大部分对象留在栈上,随着方法结束栈帧弹出而清除,这下剩余的对象中临时对象的占比就非常少了,使用分代GC的优势便不复存在。

2.为什么不使用标记复制算法?这点的原因很简单,用不到。go的底层采用先进的内存分配算法TCmalloc,通过三级缓存结构来管理内存,不同大小的对象会分配到合适大小的格子,使内存碎片大大减少。

其实还有着其他一些原因,比如go的开发团队在初期并没有足够时间去开发这种太过复杂的GC算法,这里便不做展开。

golang的GC

go的GC从开始到现在也已经走过了一段比较长的路。从早期单协程,多协程到go1.5开始使用三色标记+并行回收,再到go1.8引入混合写屏障为止才算较为成熟,经过这么多次的升级核心目的便是减少STW。

这里讲一下go1.5及之后是如何进行GC的:

三色标记法:将对象分为黑白灰三种颜色,从根节点开始,根遍历到的节点变为灰色,然后在从灰色节点开始遍历到的节点再变灰,同时把开始的灰节点变为黑色,这样遍历不到的节点为白色既需要进行清理的“垃圾”。简单来说黑色节点为其可遍历节点都是灰色的节点,而灰色节点是其可遍历节点未全变灰的节点,而白色为未遍历到的节点。

如果在stw时进行上述操作不会产生问题否则会造成对象丢失,但是如何不stw哪?这就用到了1.8加入的混合写屏障。防止对象丢失有两个原则:1.强三色不变式:所有白对象不能被黑引用 2.弱三色不变式:<-可以,但是白色对象必须有一个灰色对象指向他。在并发写入或删除时可能破坏这个原则,于是go便使用了屏障技术(并且是两种所以称混合):GC期间对于栈上创建的对象均为黑色(栈上的指针修改不触发写屏障,因初始栈已标记为黑色,且栈对象生命周期短暂,通过快照机制保证正确性),堆上删除和创建的对象都为灰色。(既然决定不了该不该删,那我下次再考虑)

垃圾回收循环主要包括以下部分:1.标记准备阶段(有STW,主要任务清扫上一阶段GC遗留对象)2.并发标记阶段(无STW)3.标记终止阶段(有STW,主要任务通过调步算法计算下次要达到的堆目标)

~前人栽树

Go八股文小解 文章被收录于专栏

Lotalot你干了什么?!没有golang八股文我们如何抗衡双招,Lotalot淡笑一声:“很简单,我自己写不就是了”说完,他气息终于不再掩饰,显露而出,Go八股文小解!

全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客企业服务