标记
垃圾回收的代码主要集中在函数 gcStart() 中。
// gcStart 是 GC 的入口函数,根据 gcMode 做处理。// 1. gcMode == gcBackgroundMode(后台运行,也就是并行), _GCoff -> _GCmark// 2. 否则 GCoff -> _GCmarktermination,这个时候就是主动 GCfunc gcStart(mode gcMode, forceTrigger bool) {...}
- STW phase 1
在 GC 开始之前的准备工作。
func gcStart(mode gcMode, forceTrigger bool) {...//在后台启动 mark workerif mode == gcBackgroundMode {gcBgMarkStartWorkers()}...// Stop The Worldsystemstack(stopTheWorldWithSema)...if mode == gcBackgroundMode {// GC 开始前的准备工作//处理设置 GCPhase,setGCPhase 还会 enable write barriersetGCPhase(_GCmark)gcBgMarkPrepare() // Must happen before assist enable.gcMarkRootPrepare()// Mark all active tinyalloc blocks. Since we're// allocating from these, they need to be black like// other allocations. The alternative is to blacken// the tiny block on every allocation from it, which// would slow down the tiny allocator.gcMarkTinyAllocs()// Start The Worldsystemstack(startTheWorldWithSema)} else {...}}
- Mark
Mark 阶段是并行的运行,通过在后台一直运行 mark worker 来实现。
func gcStart(mode gcMode, forceTrigger bool) {...//在后台启动 mark workerif mode == gcBackgroundMode {gcBgMarkStartWorkers()}}func gcBgMarkStartWorkers() {// Background marking is performed by per-P G's. Ensure that// each P has a background GC G.for _, p := range &allp {if p == nil || p.status == _Pdead {break}if p.gcBgMarkWorker == 0 {go gcBgMarkWorker(p)notetsleepg(&work.bgMarkReady, -1)noteclear(&work.bgMarkReady)}}}// gcBgMarkWorker 是一直在后台运行的,大部分时候是休眠状态,通过 gcController 来调度func gcBgMarkWorker(_p_ *p) {for {// 将当前 goroutine 休眠,直到满足某些条件gopark(...)...// mark 过程systemstack(func() {// Mark our goroutine preemptible so its stack// can be scanned. This lets two mark workers// scan each other (otherwise, they would// deadlock). We must not modify anything on// the G stack. However, stack shrinking is// disabled for mark workers, so it is safe to// read from the G stack.casgstatus(gp, _Grunning, _Gwaiting)switch _p_.gcMarkWorkerMode {default:throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")case gcMarkWorkerDedicatedMode:gcDrain(&_p_.gcw, gcDrainNoBlock|gcDrainFlushBgCredit)case gcMarkWorkerFractionalMode:gcDrain(&_p_.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit)case gcMarkWorkerIdleMode:gcDrain(&_p_.gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit)}casgstatus(gp, _Gwaiting, _Grunning)})...}}
Mark 阶段的标记代码主要在函数 gcDrain() 中实现。
// gcDrain scans roots and objects in work buffers, blackening grey// objects until all roots and work buffers have been drained.func gcDrain(gcw *gcWork, flags gcDrainFlags) {...// Drain root marking jobs.if work.markrootNext < work.markrootJobs {for !(preemptible && gp.preempt) {job := atomic.Xadd(&work.markrootNext, +1) - 1if job >= work.markrootJobs {break}markroot(gcw, job)if idle && pollWork() {goto done}}}// 处理 heap 标记// Drain heap marking jobs.for !(preemptible && gp.preempt) {...//从灰色列队中取出对象var b uintptrif blocking {b = gcw.get()} else {b = gcw.tryGetFast()if b == 0 {b = gcw.tryGet()}}if b == 0 {// work barrier reached or tryGet failed.break}//扫描灰色对象的引用对象,标记为灰色,入灰色队列scanobject(b, gcw)}}
- Mark termination (STW phase 2)
mark termination 阶段会 stop the world。函数实现在 gcMarkTermination()。
func gcMarkTermination() {// World is stopped.// Run gc on the g0 stack. We do this so that the g stack// we're currently running on will no longer change. Cuts// the root set down a bit (g0 stacks are not scanned, and// we don't need to scan gc's internal state). We also// need to switch to g0 so we can shrink the stack.systemstack(func() {gcMark(startTime)// Must return immediately.// The outer function's stack may have moved// during gcMark (it shrinks stacks, including the// outer function's stack), so we must not refer// to any of its variables. Return back to the// non-system stack to pick up the new addresses// before continuing.})...}