浅析 Golang 内存机制(内存分配、GC、内存逃逸)
内存分配
Go 程序在启动时,会向操作系统申请一定区域的内存,分为堆(Heap)和栈(Stack)。
- 堆(Heap)
- 一般来讲是人为手动进行管理,手动申请、分配和释放。所涉及的内存大小并不定,一版会存放比较大的对象。分配相对较慢涉及到的指令动作相对较多。
- 在 Go 语言中,堆内存由程序申请分配,由 GC(Garbage Collection)负责回收
- 栈(Stack)
- 由编译器进行管理,自动申请、分配、释放。一般不会太大,我们常见的函数参数、局部变量等等都会放在栈上。
- 在 Go 语言中,栈内存会随着函数的调用而进行分配,随着函数的调用结束而回收
性能上,栈内存的使用和回收会更快一些,尽管 Golang 的 GC 很高效,但过程中还会在
标记准备阶段
和标记结束阶段
进行 STW,因此,会优先使用栈内存进行分配(因为栈内存更高效,不需要 GC,所以会尽可能将内存分配到栈上),在某些特殊场景下可能会发生内存逃逸到堆上
GC(Garbage Collection)
标记清除法
在 Go 1.3 之前的版本使用的是标记清除法(mark and sweep)
,大致流程如下:
- 暂停程序业务逻辑,找出不可达对象和可达对象
- 开始标记,将程序找出的所有可达对象做上标记
- 开始清除,清除未被标记的对象
- 停止程序暂停,让程序继续执行
- 重复以上4步,直到进程生命周期结束
缺点
- STW(Stop The World) 会让程序暂停,程序会出现卡顿
- 标记需要扫描整个堆
- 清除数据会产生堆碎片
三色标记法
三色(黑白灰)标记法的核心思想是通过对堆对象的多次遍历,将堆内的所有对象通过指定条件最终区分为两种颜色,最后将其中为白色的所有对象清除掉,三色只出现在过程中。
过程
- 初始创建的对象都标记为白色
- 每次 GC 开始时,从程序根节点(rootSet)开始遍历所有对象,把遍历到的对象都标记为灰色
- 继续遍历灰色对象,将其引用的白色对象变为灰色,然后将自身变为黑色
- 重复步骤3,直到没有灰色对象
- 清除剩下的白色对象
缺点
如果三色标记法标记过程不进行 STW 保护,当下面两种条件满足时,会出现丢失对象的现象
- 白色对象被黑色对象引用
- 灰色对象与该白色对象的引用关系遭到破坏
满足这两种条件时,该白色对象实际是可用但他并不会被标记为黑色,就会在清除阶段被清除掉,为了防止这种情况发生引入了三色不变式
三色不变式
- 强三色不变式
- 不允许黑色对象引用白色对象
- 若三色不变式
- 允许黑色对象引用白色对象,但该白色对象的上游对象必须有灰色对象对其进行引用
屏障机制
屏障技术
为了实现三色不变式,引入了屏障机制
- 插入写屏障
- 触发时机
- 对象被引用时触发
- 具体实现
- 在黑色对象A对对象B进行引用时,将对象B标记为灰色
- 结果
- 满足强三色不变式,不会存在黑色对象引用白色对象
- 缺点
- 在每次 GC 过程中可能会产生一部分被染黑的垃圾对象,只有在下一次 GC 时菜户被回收
- 在标记阶段,每次进行指针赋值时都会需要进行写屏障,会增加性能开销。为了避免性能问题,可以选择关闭栈上的指针写操作的写屏障(栈空间的特点是容量小,但是要求相应速度快,因为函数调用弹出频繁使用, 所以“插入屏障”机制,在栈空间的对象操作中不使用)。当发生栈上写操作时,将栈标记为恒灰,但此举产生了灰色赋值器,需要在标记终止阶段 STW 时对这些栈进行重新扫描。
- 触发时机
- 删除写屏障
- 触发时机
- 对象被删除时触发
- 具体实现
- 被删除的对象如果自身是灰色或白色,都将其标记为灰色
- 结果
- 满足弱三色不变式,保护灰色对象到白色对象的引用不会断
- 缺点
- 回收精度不足
- 触发时机
混合写屏障
Go 在1.8版本中为了简化 GC 流程,同时减少标记终止阶段的重扫(rescan)成本,将插入屏障和删除屏障进行混合,形成混合写屏障。
混合写屏障的基本思想是,对正在被覆盖的对象进行着色,且如果当前栈未完成扫描,则同样对指针进行着色。
三色标记+混合写屏障 GC 过程
- GC 开始时优先扫描栈,将栈标记为黑色,其上可达对象全部标记为黑色,之后不再需要二次扫描,无需 STW
- GC 期间,任何栈上创建的新对象均被标记为黑色
- 被删除对象标记为灰色
- 被添加对象标记为灰色
一次完整的 GC 会分为四个阶段:标记准备,标记,结束标记,清理。在标记准备
和标记结束
阶段仍需短暂的 STW ,标记阶段会降低程序的性能
GC 的标记是广度优先
gcStart 三种触发 GC 的节点
- runtime.GC
- runtime.mallocgc
- forcegchelper
逃逸分析
TODO
TODO
TODO
TODO
TODO
TODO
Reference
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 小下同学!
评论