#基本概念
三色标记和写屏障
- 起初所有的对象都是白色
- 扫描所有的可达对象,标记为灰色,放入待处理队列
- 从队列中提取灰色对象,将其引用的对象标记成灰色放入队列,自身标记为黑色
- 写屏障监视对象内存修改,重新标色或者放回队列.
当完成全部的扫描和标记工作后,剩余的只有白色和黑色两种,分别代表待回收和活跃对象,清晰操作只需将白色对象内存回收即可。
流程
可分为以下几步:
-
扫描
a. 设置STW(stop the world,暂停用户进程) . 这将导致所有的Ps都到GC的安全点.在这里无法做内存操作.
b. 扫描所有未扫描的spans, 只有当gc被迫提前是才会有未扫描的spans -
执行mark阶段
a. 将gc的阶段从_GCoff改为_GCmark,开启写屏障(write barrier),开启辅助gc,并且将root标记工作入队. 可能在所有的Ps开启写屏障之前不会做扫描操作,写屏障使用SWT实现.
b. Start the world. 因为调度器早就运行标记worker和辅助协程执行了部分的分配,在这里GC标记工作已经完成. 写屏障将指针引用的改变和新的引用指针都置为灰色(shade). 新申请的对象会直接标记为黑色
c. gc开始root标记工作. 包括扫描所有的栈,置灰所有全局变量和heap上的指针和非heap上的运行时数据结构。每扫描一个goroutine栈就暂停一个goroutine,将栈上所有指针都置为灰色,然后将goroutine恢复.
d. gc 从灰色的work queue中放出灰色对象,扫描每个灰色对象变成灰色,并且置灰指向它的指针(引用它的对象)
e. 由于gc工作在local cache上处理, 当没有多余的root标记工作或者灰色对象时使用一个分布式算法来检测.在此时,gc转向mark termiation。 -
gc执行mark termination
a. stop the world
b. 将gcphase 改为_GCmarktermination. 然后disable workers和assists(辅助)
c. 清洗mcaches -
gc 改变sweep phase
a. 将gcphase 改为_GCoff. 设置为sweep状态,暂停写屏障
b. start the world. 从这个点开始所有新申请的对象都是白色的
c. gc 在后台并发执行回收操作 -
当足够的申请时优惠执行以上操作
初始化
mgc.go
初始化gcPercent和triggerRatio
func gcinit() {
// No sweep on the first cycle.
mheap_.sweepdone = 1
// Set a reasonable initial GC trigger.
memstats.triggerRatio = 7 / 8.0
// Set gcpercent from the environment. This will also compute
// and set the GC trigger and goal.
_ = setGCPercent(readgogc())
}
func readgogc() int32 {
p := gogetenv("GOGC")
if p == "off" {
return -1
}
if n, ok := atoi32(p); ok {
return n
}
return 100
}
在为对象分配堆内存时,mallocgc函数会检查垃圾回收出发条件,并按照相关状态启动或者参与辅助回收.
``
malloc.go
// assistG is the G to charge for this allocation, or nil if
// GC is not currently active.
var assistG *g
if gcBlackenEnabled != 0 { //辅助回收
// Charge the current user G for this allocation.
assistG = getg()
if assistG.m.curg != nil {
assistG = assistG.m.curg
}
// Charge the allocation against the G. We'll account
// for internal fragmentation at the end of mallocgc.
assistG.gcAssistBytes -= int64(size)
if assistG.gcAssistBytes < 0 {
// This G is in debt. Assist the GC to correct
// this before allocating. This must happen
// before disabling preemption.
gcAssistAlloc(assistG)
}
}
//直接分配黑色对象
// Allocate black during GC.
// All slots hold nil so no scanning is needed.
// This may be racing with GC so do it atomically if there can be
// a race marking the bit.
if gcphase != _GCoff {
gcmarknewobject(uintptr(x), size, scanSize)
}
当mcache中没有可用对象时会返回shouldhelpgc=true
,根据这个字段mallocgc中如果达到gcTrigger会开启辅助回收
if