首页 > 其他分享 >golang GC原理

golang GC原理

时间:2022-10-04 16:26:40浏览次数:48  
标签:灰色 标记 对象 STW golang 屏障 GC 原理

一、堆栈

栈(heap): 由操作系统自动分配释放。一般函数内部执行中声明的变量,函数返回时直接释放,不会引起垃圾回收,对性能无影响
堆(stack): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。堆是在程序运行时申请的动态内存,靠 GC 回收,会影响程序进程

 

二、GC的触发条件

GC触发条件有以下几种:

1 主动触发,用户代码中调用主动触发 GC
2 定期触发,默认每 2min,golang 的守护协程 sysmon 会强制触发 GC
3 内存分配量超过阈值时触发 GC

 

三、GC演变

一) GoV1.3之前 标记-清扫法

使用的是标记-清扫(Mark And Sweep)算法

 

流程:

1) 启动STW: 启动STW(Stop The World),暂停程序

2) Mark标记: 对所有存活的内存单元进行扫描,遍历所有被引用的变量,被引用的对象被标记为“被引用",没有被标记的进行回收,内存单元并不会立刻回收对象,而是将其标记为“不可达”状态。直到到达某个阈值或者到达某个时间间隔后,对其进行垃圾回收

3) Sweep清扫: 垃圾回收

4) 停止STW: 暂停STW,程序继续运行

 

过程图如下:

 

 

缺点:

1) STW程序出现卡顿

2) 标记需要扫描整个heap

3) 清除数据会产生heap碎片

 

二) GoV1.3 标记-清扫法

对标记-清扫(Mark And Sweep)算法做了优化,减少STW暂停的时间范围

 

流程:

1) 启动STW

2) Mark标记

3) 停止STW

4) Sweep清扫

 

过程图如下:

 

 

三) GoV1.5 三色标记法

三色标记将对象分为黑色、白色、灰色三种:

黑色:对象在这次GC中已标记, 且这个对象包含的子对象也已标记,表示对象是根对象可达的
白色:未标记对象,gc开始时所有对象为白色,当gc结束时,如果仍为白色,说明对象不可达,在 sweep 阶段会被清除
灰色:被黑色对象引用到的对象,对象在这次GC中已标记, 但这个对象包含的子对象未标记,灰色为标记过程的中间状态,当灰色对象全部被标记完成代表本次标记阶段结束

 

名词解释: 

根对象: 包含了全局变量, 各个goroutine栈上的变量等

标记队列: GC的标记阶段使用"标记队列"来确定所有可从根对象到达的对象都已标记

辅助GC(mutator assist): 为了防止heap增速太快, 在GC执行的过程中如果同时运行的G分配了内存, 那么这个G称为"mutator", "mutator assist"机制被要求辅助GC做一部分的工作,辅助GC做的工作有两种类型: 一种是标记(Mark), 另一种是清扫(Sweep)

 

 

1 三色标记流程:

1) 初始时所有对象都为白色

2) gc开始扫描,将所有根对象标记为灰色,放入队列

3) 遍历灰色对象,找到其引用的对象,将引用的对象标记为灰色,将灰色对象标记成黑色

4) 重复以上3步骤,直至没有灰色对象

5) 对所有白色对象进行清除

 

2 并发问题

在没有用户程序并发修改对象引用关系的情况下,使用三色标记回收没有问题,但如果用户程序在标记阶段更新了对象引用关系,会出现浮动垃圾和对象消失的情况,就可能会导致问题出现,例如: 开始扫描时发现根对象A和B, B引用了C, GC先扫描A, 然后B把C交给A, GC再扫描B, 这时C就不会被扫描到.

浮动垃圾可以在下一次GC回收,对象消失导致程序出现空指针

 

三色标记中,以下两个问题是不希望发生(实际可能发生):

1) 白色对象直接被黑色对象引用

2) 白色对象没有被其他灰色对象(直接或者间接)引用或者灰色对象与白色对象之间的可达关系遭到破坏

 

3 屏障机制

并发产生的两个问题,使用如下方式解除:

1) 强三色不变式: 不允许黑色对象引用白色对象

2) 弱三色不变式: 允许黑色对象引用白色对象,但白色对象存在其他灰色对象对它的引用(直接或间接引用)

 

4 写屏障(Write Barrier)

三色不变式对应golang中GC的写屏障,写屏障只针对指针启用, 而且只在GC的标记阶段启用

写屏障是指编译器在编译期间生成一段代码,该代码可以拦截用户程序的内存读写操作,在用户程序操作之前执行一个 hook 函数,根据 hook 函数的不同,分为 Dijkstra 插入写屏障 和 Yuasa 删除写屏障

基于对栈空间实现写屏障产生的性能损耗和实现复杂度的考虑, golang1.5的写屏障只对堆上的对象使用,不对栈上的对象使用

 

1) Dijkstra 插入写屏障

堆对象A引用B对象时,B对象被标记为灰色,满足强三色不变式

但因为栈对象没有写屏障,因此,在标记过程中,可能出现 黑色的栈对象 引用到 白色对象 的情况,所以在一轮三色标记完成后 需要开启 STW,重新对 栈上的对象 进行三色标记

 

插入写屏障的缺点

栈对象没有插入写屏障,在一轮三色标记结束时需要STW来重新扫描栈对象导致程序暂停

 

2) 删除写屏障

也叫基于快照的写屏障

如果被删除的对象为灰色,则不用处理;如果为白色,那么被标记为灰色,满足弱三色不变式,保护灰色对象到白色对象的路径不会断

 

删除写屏障的缺点

1) 回收精度低: 被删除的对象在删除的时候要将其置为灰色,而这个对象可能再也不会被其他对象引用,从而导致该对象以及它引用下的其他对象都在本轮GC后被保留下来,要待到下一轮GC才可以被清除
2) 必须在 GC 开启时执行 STW,扫描整个堆栈来记录初始快照,保证所有堆上在用的对象要么为灰色,要么处于灰色保护下,即弱三色不变式
3) 不适用于栈特别大的场景,栈越大,STW 扫描时间越长

 

5 完整的GC流程:

1) Stack scan: 收集根对象(全局变量和所有goroutine栈上的变量),该阶段会开启写屏障(Write Barrier),将其加入灰色队列

2) Mark: 标记对象,循环处理灰色队列中的对象,直到灰色队列为空,将灰色对象引用的对象标记为灰色,将灰色对象标记成黑色,此时写屏障通过mutator(即辅助GC)记录所有指针的更改

3) Mark Termination: 重新扫描部分全局变量和发生更改的栈变量(栈对象没有插入写屏障,可能会存在新的未扫描的对象),完成标记,该阶段会STW(Stop The World), 导致程序暂停

4) Sweep: 清除回收所有的白色对象

 

四) GoV1.8 三色标记法+混合写屏障法

go1.5 之后实现了插入写屏障,但是由于栈对象无法使用插入写屏障,导致扫描完之后还需要STW重新扫描栈,混合写屏障就是解决这个问题

混合写屏障(hybrid write barrier),避免了对栈re-scan的过程,极大的减少了STW的时间,混合写屏障的精度和删除写屏障的一致,比插入写屏障要低

混合写屏障扫描栈虽然没有 STW,但是扫描某个具体的栈时,还是要停止这个 goroutine (针对一个 goroutine 栈来说,是暂停扫的,要么全灰,要么全黑)

 

1 流程

1) GC扫描堆/栈上的对象并标记为黑色(逐个暂停,逐个扫描,每个栈单独扫描, 无需STW整个程序)
2) GC期间,栈上创建的新对象标记为黑色,堆上创建的新对象标记为灰色(插入写屏障),堆上删除的对象标记为灰色(删除写屏障)
3) 标记结束,重新扫描全局指针,不再rescan栈,并执行其他相关操作
4) 回收未标记对象

 

 

标签:灰色,标记,对象,STW,golang,屏障,GC,原理
From: https://www.cnblogs.com/gudanaimei/p/16624749.html

相关文章

  • 知识图谱顶会论文(ACL-2022) CAKE:用于多视图KGC的可扩展常识感知框架
    CAKE:用于多视图KGC的可扩展常识感知框架.pdf论文地址:CAKE:ScalableCommonsense-AwareFrameworkForMulti-ViewKnowledgeGraphCompletionCAKE:用于多视图知KGC的可扩......
  • [AGC041D] Problem Scores
    StOKubic神发现主要限制在第三个限制,考虑变形一下限制要求,问题转化为要求序列的\(k=\lfloor\dfrac{n}{2}\rfloor\),的前\(k+1\)项的和,大于后\(k\)项的和。动......
  • AGC014
    A若存在答案则答案是\(\mathcal{O}(\loga)\)的,直接模拟即可。B可以发现有解当且仅当给出的\(m\)条边存在欧拉回路。C\((\texttt{Easy}\1/0)\)删掉的障碍是......
  • vue面试之Composition-API响应式包装对象原理
    本文主要分以下两个部分对CompositionAPI的原理进行解读:reactiveAPI原理refAPI原理reactiveAPI原理打开源码可以找到reactive的入口,在composition-api/src/r......
  • 前端程序员学习 Golang gin 框架实战笔记之一开始玩 gin
    原文链接我是一名五六年经验的前端程序员,现在准备学习一下Golang的后端框架gin。以下是我的学习实战经验,记录下来,供大家参考。https://github.com/gin-gonic/gin1.......
  • Vue.$nextTick的原理是什么-vue面试进阶
    原理性的东西就会文字较多,请耐下心来,细细品味Vue中DOM更新机制当你气势汹汹地使用Vue大展宏图的时候,突然发现,咦,我明明对这个数据进行更改了,但是当我获取它的时候怎么是上......
  • Vue.$nextTick的原理是什么-vue面试进阶
    原理性的东西就会文字较多,请耐下心来,细细品味Vue中DOM更新机制当你气势汹汹地使用Vue大展宏图的时候,突然发现,咦,我明明对这个数据进行更改了,但是当我获取它的时候怎么是上......
  • 1、升级到gcc 7.3高版本
    https://www.cnblogs.com/FengGeBlog/p/14919920.html 1、升级到gcc7.3:yum-yinstallcentos-release-sclyum-yinstalldevtoolset-7-gccdevtoolset-7-gcc-c++d......
  • Redis核心设计原理(深入底层C源码)
    Redis基本特性1.非关系型的键值对数据库,可以根据键以O(1)的时间复杂度取出或插入关联值2.Redis的数据是存在内存中的3.键值对中键的类型可以是字符串,整......
  • 【Golang】go语言中如何控制goroutine的数量
    一、现状在Go语言中,goroutine的创建成本很低,调度效率高,Go语言在设计时就是按以数万个goroutine为规范进行设计的,数十万个并不意外,但是goroutine在内存占用方面确实具有有......