问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Go语言垃圾回收机制详解:从基础原理到优化实践

创作时间:
作者:
@小白创作中心

Go语言垃圾回收机制详解:从基础原理到优化实践

引用
CSDN
1.
https://blog.csdn.net/2401_84920056/article/details/138760014

Go语言的垃圾回收机制是其核心特性之一,了解其工作原理对于提升代码性能和优化内存管理至关重要。本文将深入探讨Go语言的垃圾回收机制,包括内存标记、三色标记法、Stop The World、写屏障、辅助GC以及垃圾回收的触发时机等内容。

内存标记(Mark)

在Go语言中,内存分配时会使用到span数据结构,该结构中包含了一个位图allocBits,用于表示每个内存块的分配情况。同时,span中还有一个位图gcmarkBits,用于标记内存块的引用情况。

如上图所示,allocBits记录了每块内存的分配情况,而gcmarkBits记录了每块内存的标记情况。标记阶段会对每块内存进行标记,有对象引用的内存标记为1(如图中灰色所示),没有引用的保持默认为0。

标记结束后进入内存回收阶段,此时会将allocBits指向gcmarkBits,这意味着只有被标记过的内存才会被视为存活,而gcmarkBits会在下一次标记时重新分配内存,这种设计非常巧妙。

三色标记法

在垃圾回收过程中,需要一个标记队列来存放待标记的对象。可以简单想象成从标记队列中取出对象,将对象的引用状态标记在spangcmarkBits,然后将对象引用到的其他对象再放入队列中。

三色标记法只是为了叙述方便而抽象出来的一种说法,实际上对象并没有颜色之分。这里的三色,对应了垃圾回收过程中对象的三种状态:

  • 灰色:对象还在标记队列中等待
  • 黑色:对象已被标记,gcmarkBits对应的位为1(该对象不会在本次GC中被清理)
  • 白色:对象未被标记,gcmarkBits对应的位为0(该对象将会在本次GC中被清理)

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

初始状态下所有对象都是白色的。接着开始扫描根对象a、b:

由于根对象引用了对象A、B,那么A、B变为灰色对象,接下来就开始分析灰色对象,分析A时,A没有引用其他对象很快就转入黑色,B引用了D,则B转入黑色的同时还需要将D转为灰色,进行接下来的分析。

如下图所示:

上图中灰色对象只有D,由于D没有引用其他对象,所以D转入黑色。标记过程结束:

最终,黑色的对象会被保留下来,白色对象会被回收掉。

Stop The World

对于垃圾回收来说,回收过程中也需要控制住内存的变化,否则回收过程中指针传递会引起内存引用关系变化,如果错误地回收了还在使用的内存,结果将是灾难性的。

Go语言中的STW(Stop The World)就是停掉所有的goroutine,专心做垃圾回收,待垃圾回收结束后再恢复goroutine。

STW时间的长短直接影响了应用的执行,时间过长对于一些web应用来说是不可接受的,这也是广受诟病的原因之一。为了缩短STW的时间,Go语言不断优化垃圾回收算法,这种情况得到了很大的改善。

垃圾回收优化

写屏障(Write Barrier)

前面说过STW目的是防止GC扫描时内存变化而停掉goroutine,而写屏障就是让goroutine与GC同时运行的手段。虽然写屏障不能完全消除STW,但是可以大大减少STW的时间。

写屏障类似一种开关,在GC的特定时机开启,开启后指针传递时会把指针标记,即本轮不回收,下次GC时再确定。

GC过程中新分配的内存会被立即标记,用的并不是写屏障技术,也即GC过程中分配的内存不会在本轮GC中回收。

辅助GC(Mutator Assist)

为了防止内存分配过快,在GC执行过程中,如果goroutine需要分配内存,那么这个goroutine会参与一部分GC的工作,即帮助GC做一部分工作,这个机制叫作Mutator Assist。

垃圾回收触发时机

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

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

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

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

定期触发GC

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

// forcegcperiod is the maximum time in nanoseconds between garbage
// collections. If we go this long without a garbage collection, one
// is forced to run.
//
// This is a variable for testing purposes. It normally doesn't change.
var forcegcperiod int64 = 2 * 60 * 1e9
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号