•
约 3 分钟
Go的内存管理简明概念
分类:programming 标签: go
目录(4)
  1. 内存回收的算法
  2. GC的工作流程
  3. 三色标记过程
  4. 垃圾回收触发机制

内存回收的算法

  • 引用计数(Python,PHP,Swift)

    • 对每一个对象维护一个引用计数,当引用该对象的对象被销毁的时候,引用计数减 1,当引用计数为 0 的时候,回 收该对象
    • 优点:对象可以很快的被回收,不会出现内存耗尽或达到某个阀值时才回收
    • 缺点:不能很好的处理循环引用,而且实时维护引用计数,有也一定的代价
  • 标记-清除(Golang)

    • 从根变量开始遍历所有引用的对象,引用的对象标记为”被引用”,没有被标记的进行回收

    • 优点:解决引用计数的缺点

    • 缺点:需要 STW(stop the word),即要暂停程序运行

  • 分代收集(Java)

    • 按照生命周期进行划分不同的代空间,生命周期长的放入老年代,短的放入新生代,新生代的回收频率高于老年代的频率

GC的工作流程

Golang GC 的大部分处理是和用户代码并行的。

  • Mark:

    • Mark Prepare: 初始化 GC 任务,包括开启写屏障 (write barrier) 和辅助 GC(mutator assist),统计root对象的任 务数量等。这个过程需要STW。

    • GC Drains: 扫描所有 root 对象,包括全局指针和 goroutine(G) 栈上的指针(扫描对应 G 栈时需停止该 G),将其 加入标记队列(灰色队列),并循环处理灰色队列的对象,直到灰色队列为空。该过程后台并行执行

  • Mark Termination:完成标记工作,重新扫描(re-scan)全局指针和栈。因为 Mark 和用户程序是并行的,所以在 Mark 过 程中可能会有新的对象分配和指针赋值,这个时候就需要通过写屏障(write barrier)记录下来,re-scan 再检查一下,这 个过程也是会 STW 的。

  • Sweep:按照标记结果回收所有的白色对象,该过程后台并行执行

  • Sweep Termination:对未清扫的 span 进行清扫, 只有上一轮的 GC 的清扫工作完成才可以开始新一轮的 GC。

三色标记过程

  • GC 开始时,认为所有 object 都是 白色,即垃圾。

  • 从 root 区开始遍历,被触达的 object 置成 灰色。

  • 遍历所有灰色 object,将他们内部的引用变量置成 灰色,自身置成 黑色

  • 循环第 3 步,直到没有灰色 object 了,只剩下了黑白两种,白色的都是垃圾。

  • 对于黑色 object,如果在标记期间发生了写操作,写屏障会在真正赋值前将新对象标记为 灰色。

  • 标记过程中,mallocgc 新分配的 object,会先被标记成 黑色 再返回。

三色标记过程:对象沿引用链从白色经灰色逐步标记为黑色

垃圾回收触发机制

  • 内存分配量达到阀值触发 GC

    • 每次内存分配时都会检查当前内存分配量是否已达到阀值,如果达到阀值则立即启动 GC。

      • 阈值 = 上次 GC 内存分配量 * 内存增长率

      • 内存增长率由环境变量 GOGC 控制,默认为 100,即每当内存扩大一倍时启动 GC。

  • 定期触发 GC

    • 默认情况下,最长 2 分钟触发一次 GC,这个间隔在 src/runtime/proc.go:forcegcperiod 变量中被声明
  • 手动触发

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