golang 的 GC 经历过很多次变革, 主要大更新的版本是
标记清除: v1.3三色标记: v1.5混合写屏障+三色标记: v1.8golang 的 GC 不断的提升并发性能并且**减少STW (Stop The World)**的时间
标记清除 步骤 stw 暂停程序mark: 标记可达对象(分类出可对象和不可达对象)sweep: 清除不可达对象stw 结束 怎么 mark 标记可达对对象?从程序的根节点往下面访问, 能遍历到的就是可达对象, 遍历不到的就是不可达的对象
标记清除算法的优化如下图所示: ACE 都是可达对象, BD 是不可达对象
标记清除算法整个 GC 都是在 STW, STW 的时间过长了
为了减少 STW 的时间, golang 将 STW 的范围缩小
只是在 mark 的是否 STW, sweep 和程序并发执行
但是整个的 STW 还是挺长的, 特别是当需要 mark 标记的对象越多, 需要 STW 的时间越多
于是 go 1.5 就用三色标记法来动态的滚动式的做 gc 将一次长的 stw 分散到多次短暂 stw 中去
三色标记 步骤 遍历根对象的第一层可达对象标记为灰色, 不可达默认白色将灰色对象的下一层可达对象标记为灰色, 自身标记为黑色多次重复步骤2, 直到灰色对象为0, 只剩下白色对象和黑色对象sweep 白色对象 示例遍历根对象的第一层可达对象标记为灰色, 不可达默认白色
将灰色对象 A 的下一层可达对象标记为灰色, 自身标记为黑色
继续遍历灰色对象的下层对象,重复步骤2
继续遍历灰色对象的下层对象,重复步骤2
灰色对象为0, 清理白色对象
三色标记对象丢失如果 gc 期间不 stw 的话有可能对象丢失
一个黑色对象在 gc 期间链接了白色对象, 白色对象又没有任何的灰色对象可达就会导致对象的丢失
举个例子
(1) 上图刚刚标记完, 准备清除(此时程序并发执行 E 对象链接了B对象)
此时按道理 BD 式可达的, 但是此时 BD 会被当成垃圾清理掉,造成对象丢失
三色标记对象丢失解决对象丢失是在GC里面绝对不被允许的, 可以暂时存在垃圾但是不能丢失
所以还是要 STW
为了解决这个 STW 的问题, 引入了强三色不变式和弱三色不变式, 只要满足强三色不变式和弱三色不变式的任意一种就能解决 STW 的问题
强三色不变式: 一个黑色对象在 gc 期间链接了白色对象弱三色不变式: 黑色对象链接的白色对象又没有任何的灰色对象可达三色标记+混合写屏障就能解决这个问题
三色标记 + 混合写屏障 步骤 STW 扫描栈, 将可达对象标记为黑色(混合写屏障也是要STW的, 网上很多文章都没说, scan stack 的时候要 STW,只是混合写屏障去掉了 rescan stack 这个步骤,时间在 1ms 左右)gc 期间 stack 创建的对象都是灰色gc 期间在堆添加混合写屏障(链接/删除链接 元素都标记为灰色,屏障会略微减少程序性能, 因为会加一层) 示例: 解决对象丢失问题(1) 上图刚刚标记完, 准备清除(此时程序并发执行 E 对象链接了B对象)
链接对象会经过混合写屏障, 新插入的元素会标记为灰色
(2) 接下来按照三色标记继续遍历即可
go GC 和 Java GC 有什么区别golang 的 gc 的不断演化通过将 STW 分散化大大减少 STW 的时间
最新版本的 golang 的 gc 基本都是在 1ms 以内, 但是这样故意的设计是牺牲吞吐量换来的
gc 的频率会比其他的垃圾回收更高比如 jvm 和 .net高
go 目前的混合写屏障 + 三色标记标记在 gc 期间的屏障也会给性能带来一定的损耗
但是总体的损耗目前的机器配置完全能 cover,
go 的开发效率很高
go 的主要应用领域不是 cpu 敏感型,在高 IO 的领域有很好的性能
go 要避免跟 jvm 和 .net 这样给高吞吐的 gc 正面竞争, 才能发挥出 go 超短 stw 的优势
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)