学习Golang GC 必知的几个知识点
By admin
- One minute read - 183 words对于gc的介绍主要位于 [src/runtime/mgc.go](https://github.com/golang/go/blob/go1.16.2/src/runtime/mgc.go)
,以下内容是对注释的翻译。
GC 四个阶段
通过源文件注释得知GC共分四个阶段:
- GC 清理终止 (
GC performs sweep termination
) a.Stop the world
, 每个P 进入GCsafepoint
(安全点),从此刻开始,万物静止。 b. 清理未被清理的span,如果GC被强制执行时才会出现这些未清理的span - GC 标记阶段(
GC performs the mark phase
) a. 将gc标记从_GCoff
修改为_GCmark
,开启写屏障(write barries
)和 协助助手(mutator assists
),将根对象放入队列。 在STW期间,在所有P都启用写屏障之前不会有什么对象被扫描。 b.Start the world
(恢复STW)。标记工作线程和协助助手并发的执行。对于任何指针的写操作和指针值,都会被写屏障覆盖,使新分配的对象标记为黑色。 c. GC 执行根标记工作。包括扫描所有的栈,全局对象和不在堆数据结构中的堆指针。每扫描一个栈就会导致goroutine停止,把在栈上找到的所有指针置灰色,然后再恢复goroutine运行。 d. GC 遍历队列中的每个灰色对象,扫描完以后将灰色对象标记为黑色,并将其指向的对象标记为灰色。 e. 由于GC工作在分布本地缓存中,采用了一种 “分布式终止算法(distributed termination algorithm
)” 来检测什么时候没有根对象或灰色对象。在这个时机GC会转为标记中止(mark termination
)。 - 标记终止(
GC performs mark termination
) a.Stop the world
,从此刻开始,万物静止 b. 设置阶段为_GCmarktermination
,并禁用 工作线程worker和协助助手 c. 执行清理,flush cache - 清理阶段(
GC performs the sweep phase
) a. 设置清理阶段标记为_GCoff
,设置清理状态禁用写屏障 b.Start the world
(恢复STW),从现在开始,新分配的对象是白色的。如有必要,请在请在使用前扫描清理 c. GC在后台执行并发扫描,并响应分配
整个GC共四个阶段,每次开始时从上到下执行。第一步是清理上次未清理完的span,而不是直接标记阶段。具体的流程可以参考 runtime.GC()
函数
在每轮GC都会有两次STW发生,每个阶段都会发生一次STW相关的操作。
并发清理(Concurrent sweep)
清除阶段与程序执行是并发执行的。后台有一个goroutine并发的对heap进行清理,按span为是小单位一个接一个的清理。当 STW 标记终止后,所有的span将被标记为”needs seeping
“
后台goroutine进行清理时,是按span为单位,one-by-one
的执行。
为了避免当存在未清理的span时,应用会向系统申请更多的内存的问题,当一个goroutine需要另外span时(当前goroutine对应的P没有需要的span),它首先通过清理的方式获取相应的内存大小。当一个goroutine需要分配一些小对象 span 时,会先清理相同大小span ,直到至少释放了一个对象;当一个goroutine需要从heap上分配大对象时,它会清理span,直到至少释放一样大小的页到heap上。有一种情况例外:如果一个 goroutine 清理释放了两个不相邻的one-page span到堆上,将会导致重新分配一个新的two-page span,
GC速率(GC Rate)
下次GC是在我们分配了与已经使用的内存量成一定比例的之后。此值受 GOGC
环境变量控制,默认为100,即增涨率为100%。如果 GOGC=100
,当前我们使用了4M,下次GC时将会在内存达到8M的时候发生(标记在 memstats
.next_gc
变量)。这使GC成本保持线性与分配成本的比例。调整GOGC只会改变线性常数(以及使用的额外内存量)。
GC 注意事项
GC期间,将阻塞任何的调用,直到GC结束,即STW期间万物静止的状态,所以会阻塞整个程序的运行。
一次完整的GC包含 sweep termination
, mark
,mark termination
和 sweep
。对于 runtime.GC()
函数来说直到这四个阶段全部完成才会返回继续执行,否则程序将一直处于阻塞状态。对于要切换到 sweep
阶段,必须等待 mark terminatio
n 阶段全部完成才可以。
系统会记录GC的次数,这个值会在进入 sweep
阶段时更新+1。
对于触发GC 的几个条件请参考: https://blog.haohtml.com/archives/23911
GC 的三种模式
三种模式:
- (1)gcBackgroundMode,默认模式,标记与清扫过程都是并发执行的;
- (2)gcForceMode,只在清扫阶段支持并发;
- (3)gcForceBlockMode,GC全程需要STW。
// gcMode indicates how concurrent a GC cycle should be.
type gcMode int
const (
gcBackgroundMode gcMode = iota // concurrent GC and sweep
gcForceMode // stop-the-world GC now, concurrent sweep
gcForceBlockMode // stop-the-world GC now and STW sweep (forced by user)
)