Go 垃圾回收原理

Golang源码探索(三) GC的实现原理

  • 引用计数:对每个对象维护一个引用计数,当引用该对象的对象被销毁时,引用计数减1,当引用计数器为0是回收该对象。

    • 优点:对象可以很快的被回收,不会出现内存耗尽或达到某个阀值时才回收。
    • 缺点:不能很好的处理循环引用,而且实时维护引用计数,有也一定的代价。
    • 代表语言:Python、PHP、Swift
  • 标记-清除:从根变量开始遍历所有引用的对象,引用的对象标记为"被引用",没有被标记的进行回收。
    • 优点:解决了引用计数的缺点。
    • 缺点:需要STW,即要暂时停掉程序运行。
    • 代表语言:Golang(其采用三色标记法)
  • 分代收集:按照对象生命周期长短划分不同的代空间,生命周期长的放入老年代,而短的放入新生代,不同代有不能的回收算法和回收频率。
    • 优点:回收性能好
    • 缺点:算法复杂
    • 代表语言: JAVA

标记

在之前的Go语言——内存管理一文中,分析过span是内存管理的最小单位,所以猜测gc的粒度也是span。

type mspan struct {
// allocBits and gcmarkBits hold pointers to a span's mark and
// allocation bits. The pointers are 8 byte aligned.
// There are three arenas where this data is held.
// free: Dirty arenas that are no longer accessed
// and can be reused.
// next: Holds information to be used in the next GC cycle.
// current: Information being used during this GC cycle.
// previous: Information being used during the last GC cycle.
// A new GC cycle starts with the call to finishsweep_m.
// finishsweep_m moves the previous arena to the free arena,
// the current arena to the previous arena, and
// the next arena to the current arena.
// The next arena is populated as the spans request
// memory to hold gcmarkBits for the next GC cycle as well
// as allocBits for newly allocated spans.
//
// The pointer arithmetic is done "by hand" instead of using
// arrays to avoid bounds checks along critical performance
// paths.
// The sweep will free the old allocBits and set allocBits to the
// gcmarkBits. The gcmarkBits are replaced with a fresh zeroed
// out memory.
allocBits *gcBits
gcmarkBits *gcBits
}

  

bitmap

如图所示,通过gcmarkBits位图标记span的块是否被引用。对应内存分配中的bitmap区。

三色标记

  • 灰色:对象已被标记,但这个对象包含的子对象未标记
  • 黑色:对象已被标记,且这个对象包含的子对象也已标记,gcmarkBits对应的位为1(该对象不会在本次GC中被清理)
  • 白色:对象未被标记,gcmarkBits对应的位为0(该对象将会在本次GC中被清理)

例如,当前内存中有A~F一共6个对象,根对象a,b本身为栈上分配的局部变量,根对象a、b分别引用了对象A、B, 而B对象又引用了对象D,则GC开始前各对象的状态如下图所示:

  1. 初始状态下所有对象都是白色的。
  2. 接着开始扫描根对象a、b; 由于根对象引用了对象A、B,那么A、B变为灰色对象,接下来就开始分析灰色对象,分析A时,A没有引用其他对象很快就转入黑色,B引用了D,则B转入黑色的同时还需要将D转为灰色,进行接下来的分析。
  3. 灰色对象只有D,由于D没有引用其他对象,所以D转入黑色。标记过程结束
  4. 最终,黑色的对象会被保留下来,白色对象会被回收掉。
 
 

STW

stop the world是gc的最大性能问题,对于gc而言,需要停止所有的内存变化,即停止所有的goroutine,等待gc结束之后才恢复。

触发

  • 阈值:默认内存扩大一倍,启动gc
  • 定期:默认2min触发一次gc,src/runtime/proc.go:forcegcperiod
  • 手动:runtime.gc()

go

  1. Sweep Termination: 对未清扫的span进行清扫, 只有上一轮的GC的清扫工作完成才可以开始新一轮的GC
  2. Mark: 扫描所有根对象, 和根对象可以到达的所有对象, 标记它们不被回收
  3. Mark Termination: 完成标记工作, 重新扫描部分根对象(要求STW)
  4. Sweep: 按标记结果清扫span

目前整个GC流程会进行两次STW(Stop The World), 第一次是Mark阶段的开始, 第二次是Mark Termination阶段.

  • 第一次STW会准备根对象的扫描, 启动写屏障(Write Barrier)和辅助GC(mutator assist).
  • 第二次STW会重新扫描部分根对象, 禁用写屏障(Write Barrier)和辅助GC(mutator assist).

需要注意的是, 不是所有根对象的扫描都需要STW, 例如扫描栈上的对象只需要停止拥有该栈的G.
从go 1.9开始, 写屏障的实现使用了Hybrid Write Barrier, 大幅减少了第二次STW的时间.

写屏障

因为go支持并行GC, GC的扫描和go代码可以同时运行, 这样带来的问题是GC扫描的过程中go代码有可能改变了对象的依赖树。

例如开始扫描时发现根对象A和B, B拥有C的指针。

  1. GC先扫描A,A放入黑色
  2. B把C的指针交给A
  3. GC再扫描B,B放入黑色
  4. C在白色,会回收;但是A其实引用了C。

为了避免这个问题, go在GC的标记阶段会启用写屏障(Write Barrier).

启用了写屏障(Write Barrier)后,

  1. GC先扫描A,A放入黑色
  2. B把C的指针交给A
  3. 由于A在黑色,所以C放入灰色
  4. C没有子对象,放入黑色
  5. 扫描B,B没有子对象,放入黑色

即使A可能会在稍后丢掉C, 那么C就在下一轮回收。

开启写屏障之后,当指针发生改变, GC会认为在这一轮的扫描中这个指针是存活的, 所以放入灰色

root

首先标记root跟对象,根对象的子对象也是存活的。

根对象包括:全局变量,各个G stack上的变量等。

Golang——垃圾回收GC的更多相关文章

  1. Golang——垃圾回收GC(2)

    1 垃圾回收中的重要概念 1.1 定义 In computer science, garbage collection (GC) is a form of automatic memory manag ...

  2. golang 垃圾回收 gc

    http://ruizeng.net/golang-gc-internals/ 摘要 在实际使用go语言的过程中,碰到了一些看似奇怪的内存占用现象,于是决定对go语言的垃圾回收模型进行一些研究.本文对 ...

  3. 【GoLang】golang垃圾回收 & 性能调优

    golang垃圾回收 & 性能调优 参考资料: 如何监控 golang 程序的垃圾回收_Go语言_第七城市 golang的垃圾回收(GC)机制 - 两只羊的博客 - 博客频道 - CSDN.N ...

  4. golang垃圾回收

    常见GC算法 我总结了一下常见的 GC 算法.分别是:引用计数法.Mark-Sweep法.三色标记法.分代收集法. 1. 引用计数法 原理是在每个对象内部维护一个整数值,叫做这个对象的引用计数,当对象 ...

  5. 从C#垃圾回收(GC)机制中挖掘性能优化方案

    GC,Garbage Collect,中文意思就是垃圾回收,指的是系统中的内存的分配和回收管理.其对系统性能的影响是不可小觑的.今天就来说一下关于GC优化的东西,这里并不着重说概念和理论,主要说一些实 ...

  6. 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配

    垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...

  7. Java 垃圾回收(GC) 泛读

    Java 垃圾回收(GC) 泛读 文章地址:https://segmentfault.com/a/1190000008922319 0. 序言 带着问题去看待 垃圾回收(GC) 会比较好,一般来说主要 ...

  8. 类装饰器,元类,垃圾回收GC,内建属性、内建方法,集合,functools模块,常见模块

    '''''''''类装饰器'''class Test(): def __init__(self,func): print('---初始化---') print('func name is %s'%fu ...

  9. 性能测试三十五:jvm垃圾回收-GC

    垃圾回收-GC 三个问题 哪些内存需要回收? 什么时候回收? 如何回收? YoungGC和FullGC: 新生代引发的GC叫YoungGC 老年代引发的GC叫FullGC FullGC会引起整个Jvm ...

随机推荐

  1. HDU2473 Junk-Mail Filter 并查集

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - HDU2473 题意概括 一堆点. 要你支持合并两组点.分离某组点中的一个,这两种操作. 点数<=100 ...

  2. BZOJ4972 八月月赛 Problem B 小Q的方格纸 二维前缀和

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4972 八月月赛Problem B 题目概括 一个矩阵,一坨询问,问矩阵中一个特定方向的等腰直角三角 ...

  3. 解决celipse中mybatis使用的时候xml没有提示的问题

    文件下载好以后点击Add会出现如下界面 上面是添加mapper的插件,再找到config的配置文件.和上面相似的操作安装以后就可以了

  4. Free DIY Tour HDU1224

    一道很好的dfs加储存路径的题目  :路径保存:每次dfs都存i 当大于max时 将临时数组保存到答案数组 并不是当 当前值大于最大值时更新路径 还要加上一个条件:能回去 #include<bi ...

  5. 【Java】 剑指offer(55-2) 平衡二叉树

      本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入一棵二叉树的根结点,判断该树是不是平衡二叉树.如果某二叉树 ...

  6. printf的执行顺序

    printf()函数的参数,在printf()函数读取时是从左往右读取的,然后将读取到的参数放到栈里面去, 最后读取到的就放在栈顶,处理参数的时候是从栈顶开始的,所以是从右边开始处理的. --prin ...

  7. Nuxt.js 如何在 asyncData中 请求数据 ,并将拿到的数据传给子组件

    说明:同接口请求一样,也可以进行数据的处理:return  中 左侧的变量  可以直接拿到在页面上使用,也可以传递给子组件 下面再给出一段代码,方便觉得有用的.却又不想手敲的朋友们: async as ...

  8. Codeforces.911F.Tree Destruction(构造 贪心)

    题目链接 \(Description\) 一棵n个点的树,每次可以选择树上两个叶子节点并删去一个,得到的价值为两点间的距离 删n-1次,问如何能使最后得到的价值最大,并输出方案 \(Solution\ ...

  9. LED类代码

      /* led.c文件 标题: 点亮一个了LED灯 电路:开发板中P2口已接到LED灯的阴极 */ #include <reg52.h> #include "led1.h&qu ...

  10. memcached 数据库

    安装 [root@node2 ~]# yum install memcached 启动 [root@node2 ~]# systemctl start memcached 连接 [root@node2 ...