go的GC理解

in #cn14 days ago

GC实现方式有哪些

GC (Garbage Collection)泛指垃圾回收机制,在高级编程语言中,实现自动化的内存管理(内存分配与回收)。

  • 自动GC
    可以使得开发者更关注业务代码,无需管理程序内存分配与回收,有效的提高开发效率。
  • 手动GC
    手动GC可以更精确的通过API管理内存,可以极致利用资源。

GC实现方式

主要有两类,包括 追踪(Tracing) 和 引用计数 (Reference Counting)

  • 追踪式GC
    根对象出发,根据对象之间的引用关系一步步扫描整个堆。

根对象: GC在追踪过程中,最先检查的对象。

  1. 全局变量 (编译阶段就能确定,生命周期同整个应用程序)
  2. 执行栈 (协程独立,栈上变量以及持有的逃逸对象堆上的引用)
  3. 寄存器 (指针,堆内存区块等)

实现方式

  1. 标记清除(产生内存碎片)
  2. 标记整理
  3. 等块复制copy清理
  4. 分代
  5. 增量(组合多方式)
  6. 并发(gc+应用程序并行)
  • 引用计数式
    给每个对象维护一个引用计数器,当对象被引用时,计数器+1,反之-1, 若是引用计数器=0表示可以清理回收。

GO的GC实现方式

golang中的gc,无分代(没有代际之分),不整理(回收过程中不对对象进行移动整理),并发的三色标记清除算法。

此处GC的并发是指,某个阶段GC可以与用户代码并发执行。

为什么不使用分代GC

  1. 逃逸分析特性使得分代GC在GO中没有优势。(分代重点处理新生代对象,在GO中短期活跃对象在栈,随着执行栈回收而被处理掉,不太需要GC的干预。)
  2. GO中使用的tcmall算法基本没有内存碎片,无需GC对内存碎片进行整理。
  3. GO中的GC目标更着重于并发执行,分代在STW上没有明显优势,与对象存活时间,对象大小没有直接关系。

Go中的GC流程

image.png

STW (Stop The World) 是指在GC过程中,为了保证GC算法实现的正确性,防止内存动态分配调整等问题,不可避免需要停止赋值器进一步操作对象的过程,整个用户代码是被停止执行,STW的延迟影响程序并发性能。

三色标记法
在1.5版本引入三色标记法,通过三个阶段的标记来确定需要清除的对象。

重要的标记节点(MARK)和清除阶段(SWEEP)。

  • 白色: 表示未被垃圾回收器访问到的对象。(初始分配内存对象都是白色)
  • 灰色:表示已被垃圾回收器访问到的对象,但其子对象还未被访问到。
  • 黑色: 表示已被垃圾回收器访问到的对象,并且其子对象也都被访问到。
  1. 从根节点开始遍历所有对象,将根对象放入“灰色”集合。
  2. 遍历灰色集合,开启新一轮遍历(一层),将灰色对象可直达的对象放入“灰色集合”;将上一轮灰色对象(已遍历)放入“黑色集合”。
  3. 重复遍历灰色集合,直到灰色集合为空。

Q: 如果三色标记过程中,不通过STW暂停用户程序会出现什么问题?

image.png

A: 在标记过程中,用户代码的动态执行,会影响标记对象的引用关系。会出现对象标记扫描不准确问题。

在标记阶段,在未扫描obj2的时候,obj1移除对obj2的引用,正常情况下obj2,obj3是要被回收;但是用户程序代码又赋值将已经标记为黑色的obj6对象引用指向obj2。obj6子对象在这轮标记过程中,不会扫描obj2, 此刻obj2为白色对象,会被GC回收掉,不符合预期。


Q: 如何确保在不会GC误杀情况下,减少STW的时间?

首先,先明确什么情况下,对象会被GC误杀或者丢失?

  1. 一个白色对象被黑色对象引用 (黑色对象子对象,引用对象不会参与GC扫描)
  2. 灰色对象同时失去了白色对象的可达关系。(导致白色对象不会GC扫描)

为解决问题,在1.5版本引入屏障机制,在满足以下两者任意一个条件时候,就能保证GC对象不丢失,不误杀。

  • 强三色不变式
    强制性不允许黑色对象引用白色对象。

image.png

  • 弱三色不变式

允许黑色对象引用白色对象,但是必须满足一定的条件:

  1. 白色对象必须存在其他灰色对象对它的引用。
  2. 或者这个白色对象的链路上游存在灰色对象。

image.png

如何实现强弱三色不变式

引入屏障技术,包括插入屏障混合写屏障

屏障本质上是程序执行过程中增加额外的判断机制;满足一定条件就使用类似回调或者hook通知GC处理。

  • 插入屏障(insertion barrier)

插入写屏障又称为增量更新屏障;插入到黑色对象后的对象会被保守的标记为非白色对象。

流程如下图:
image.png

  1. 当obj1,obj5从灰色对象后开始新一轮扫描,obj1和obj5会变成黑色。
  2. 黑色对象有新对象的引用,如下图的obj4,obj7,新增对象应该是白色,但是为了满足强三色不变式条件,需要插入屏障来将两者变成灰色处理。
  3. 在下一轮扫描过程成,对象obj4,obj7就会变成黑色对象。

image.png

  • 删除屏障

删除屏障又称之为给予起始快照的屏障。
当一个白色或者灰色对象的引用被移除时,该对象被标记为灰色,满足弱三色不变式条件。

image.png
会多一些问题:
被解引用的对象,会从白色标记为灰色,假设该对象确实没使用,则多了一轮扫描。
但是好处是能尽量减少STW的时间。

  • 混合写屏障
    插入屏障和删除屏障在分别独立使用的时候,会存在一些问题:
  1. 为了提高栈空间性能,栈上不使用屏障机制,如果堆栈存在互相引用的情况,只使用插入屏障,栈空间需要STW,扫描一遍栈空间对象。
  2. 删除写屏障回收精度低,需要多一轮扫描回收对象。

在1.8 版本设计混合写屏障机制,可以避免堆栈的重新扫描,大大减少STW时间(<ms级别),同时结合了插入屏障和删除屏障的两者优势。(并发三色标记法)

混合写屏障的几个规则

  • GC启动开始优先扫描栈上的对象,并将可达对象全部标记为黑色。(包括用户并发代码创建新对象,避免栈空间重复扫描)
  • 当对象删除时候出触发删除写屏障,将删除的对象标记为灰色。
  • 当对象新增时候,触发插入写屏障,将新增对象(正常是白色)标记为灰色。

image.png

栈上对象(都是黑色)的引用修改调整,不会有屏障技术应用,不用重新扫描栈上对象。

image.png


若是存在栈上对象引用堆上白色对象(未被扫描),则无需做操作;若是被引用堆上对象被解引用,触发删除屏障,白色对象变灰色对象,此刻栈上的引用也保持正确性。

image.png

Coin Marketplace

STEEM 0.20
TRX 0.12
JST 0.028
BTC 64097.37
ETH 3476.43
USDT 1.00
SBD 2.53