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. POJ 1064 1759 3484 3061 (二分搜索)

    POJ 1064 题意 有N条绳子,它们长度分别为Li.如果从它们中切割出K条长度相同的绳子的话,这K条绳子每条最长能有多长?答案保留小数点后2位. 思路 二分搜索.这里要注意精度问题,代码中有详细说 ...

  2. canvas放射粒子效果

    这个也是别人的代码,就不多介绍了 写了些注释 body { overflow:hidden; margin:0; padding:0; background-color:#222222 } </ ...

  3. 7-6 Bandwidth UVA140

    没有清空向量导致debug了好久 这题难以下手  不知道怎么dfs 原来是用排序函数. letter[n]=i; id[i]=n++; 用来储存与设置标记十分巧妙 for(;;) { while(s[ ...

  4. TotoriseGit安装

    1.前提 前提是有安装过git. 2.下载 3.安装 二:参考的文档 1.不错的文档 https://www.cnblogs.com/xinlj/p/5978730.html http://blog. ...

  5. 'utf-8' codec can't decode byte 0xc8 in position 0

    今天学习python中使用jieba库,遇到了错误:“UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc8 in position 0: i ...

  6. hdu3047 Zjnu Stadium【带权并查集】

    <题目链接> <转载于 >>> > 题目大意: 有n个人坐在zjnu体育馆里面,然后给出m个他们之间的距离, A B X, 代表B的座位比A多X. 然后求出这 ...

  7. @RequestParam注解使用:Name for argument type [java.lang.String] not available, and parameter name information not found in class file either.

    详细错误信息 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Re ...

  8. go标准库-log包源码学习

    log包是go语言提供的一个简单的日志记录功能,其中定义了一个结构体类型 Logger,是整个包的基础部分,包中的其他方法都是围绕这整个结构体创建的. Logger结构 Logger结构的定义如下: ...

  9. asp.net core模块学习

    一.配置管理 二.管道 三.认证与授权 四.MVCDemo 五.IdentityServer4 一.配置管理 1,读取内存配置 using System; using Microsoft.Extens ...

  10. Python中应用SQL及SQLAlchemy(一)

    以SQLit3为例: import sqlite3 conn = sqlite3.connect('db.sqlite3') #获取游标对象 cur = conn.cursor() #执行一系列SQL ...