1. Golangs/

Golang垃圾回收三色标记原理详解🤣

·173 字·1 分钟· loading
golang golang
demo007x
作者
demo007x

Golang垃圾回收三色标记原理详解🤣
#

golang.jpeg

垃圾回收(Garbage Collection,简称 GC)是编程语言中自动的内存管理机制,垃圾回收,垃圾指的是不再需要的内存块,如果不及时清理就没有办法再利用。

垃圾回收算法以及优缺点!
#

常见的垃圾回收算法有:

  • 引用计数:每个对象维护一个引用计数,如果这个对象被销毁,则计数 -1 ,当计数为 0 时,回收该对象。

    优点:对象可以很快被回收,不会出现内存耗尽或到达阀值才回收。

    缺点:不能很好的处理循环引用

  • 标记-清除:从根变量开始遍历所有引用的对象,引用的对象标记“被引用”,没有被标记的则进行回收。

    优点:解决了引用计数的缺点。

    缺点:需要 STW(stop the world),暂时停止程序运行。

  • 分代收集:按照对象生命周期长短划分不同的代空间,生命周期长的放入老年代,短的放入新生代,不同代有不同的回收算法和回收频率。

    优点:回收性能好

    缺点:算法复杂

三色标记清除算法 
#

Golang中采用 三色标记清除算法(tricolor mark-and-sweep algorithm)  进行GC。由于支持写屏障(write barrier)了,GC过程和程序可以并发运行。

三色标记清除算核心原则就是根据每个对象的颜色,分到不同的颜色集合中,对象的颜色是在标记阶段完成的。三色是黑白灰三种颜色,每种颜色的集合都有特别的含义:

  • 黑色集合:该集合下的对象没有引用任何白色对象(即该对象没有指针指向白色对象)

  • 白色集合:扫描标记结束之后,白色集合里面的对象就是要进行垃圾回收的,该对象允许有指针指向黑色对象。

  • 灰色集合:可能有指针指向白色对象。它是一个中间状态,只有该集合下不在存在任何对象时候,才能进行最终的清除操作

三色标记的过程
#

  1. 初始状态下所有对象都是白色的。
  2. 从根节点开始遍历所有对象,把遍历到的对象变成灰色对象(备注:这里变成灰色对象的都是根节点的对象)。
  3. 遍历灰色对象,将灰色对象引用的对象(备注:这里指的是灰色对象引用到的所有对象,包括灰色节点间接引用的那些对象)也变成灰色对象,然后将遍历过的灰色对象变成黑色对象。
  4. 循环步骤 3,直到灰色对象全部变黑色。
  5. 通过写屏障(write-barrier)检测对象有变化,重复以上操作(备注:因为 mark 和用户程序是并行的,所以在上一步执行的时候可能会有新的对象分配,写屏障是为了解决这个问题引入的)。
  6. 收集所有白色对象(垃圾)。

图例说明标记过程
#

  1. 开始阶段,所有对象均标记为白色,调用情况为:

        root -> F     E      G
         |                   |
         A                   H
       / | \
      B  D  C
    

image.png

  1. GC 开始扫描,从根节点开始遍历,发现只有 A 和 F 是根节点,于是将 A、F 从变为灰色对象。

image.png

  1. GC 继续扫描灰色对象,会将灰色对象的节点中引用的节点也变为灰色对象,A 节点引用的节点 B、C、D 会被变为灰色对象,接着 A 的所有子节点遍历完毕,便会变为黑色对象,而 F 节点没有子节点,也会变为黑色对象。

image.png

  1. GC 会循环遍历灰色对象,直到灰色对象之中没有节点为止,在本例中,发现 B、C、D 都没有子节点是白色,便将 B、C、D 都变为黑色对象。

image.png

  1. 剩下 E、G、H 为白色对象,GC 便进行回收这些白色对象。

image.png

  1. 上面的垃圾回收结束之后,GC 会在进行一步操作,也就是将黑色对象重新变色成白色对象,供下一次垃圾回收使用。

image.png

STW
#

Golang 1.5 以前的 STW(Stop The World)是停掉所有的 goroutine,做垃圾回收任务,待垃圾回收结束后再恢复 goroutineSTW 时间的长短直接影响了应用的执行的性能。为了缩短 STW 的时间,Golang 不断优化垃圾回收算法,采用三色标记清除算法后,这种情况得到了很大的改善。

垃圾回收优化
#

写屏障(Write Barrier)
#

STW 目的是防止 GC 扫描时内存变化而停掉 goroutine,而写屏障就是让 goroutine 与 GC 同时运行的手段。虽然写屏障不能完全消除 STW,但是可以大大减少 STW 的时间。写屏障类似一种开关,在 GC 的特定时机开启,开启后指针传递时会把指针标记,即本轮不回收,下次 GC 时再确定。GC 过程中新分配的内存会被立即标记,用的并不是写屏障技术,也即 GC 过程中分配的内存不会在本轮 GC 中回收。

辅助 GC(Mutator Assist)
#

为了防止内存分配过快,在 GC 执行过程中,如果 goroutine 需要分配内存,那么这个 goroutine 会参与一部分 GC 的工作,即帮助 GC 做一部分工作,这个机制叫做 Mutator Assist

垃圾回收触发时机
#

  1. 每次内存分配时都会检查当前内存分配量是否已达到阈值,如果达到阈值则立即启动 GC。内存增长率由环境变量 GOGC 控制,默认为 100,即每当内存扩大一倍时启动 GC。阀值 = 上次GC内存分配量 * 内存增长率

  2. 默认情况下,最长 2 分钟触发一次 GC。

  3. 程序代码中也可以使用 runtime.GC() 来手动触发 GC。这主要用于 GC 性能测试和统计。

Related

链表数据结构
数据结构与算法 数据结构与算法 链表 golang
什么是链表?> 链表是一种线性数据结构,看起来像节点链,其中每个节点都是不同的元素。与数组不同,链表元
golang 访问者模式讲解和代码示例
设计模式 设计模式 golang
访问者是一种行为设计模式, 允许你在不修改已有代码的情况下向已有类层次结构中增加新的行为。
golang 模板方法模式讲解和代码示例
设计模式 设计模式 golang
模版方法是一种行为设计模式, 它在基类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。