上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
3.4.1 流程
首先,调用 startCycle 开始新周期。按 25% CPU 利用率计算正式工和小时工数值。
// mgc.go const gcBackgroundUtilization = 0.25
// mgc.go // startCycle resets the GC controller's state and computes estimates // for a new GC cycle. func (c *gcControllerState) startCycle(markStartTime int64, procs int) { // 确保目标比 heapLive 大一点。 if goexperiment.PacerRedesign { if c.heapGoal < c.heapLive+64<<10 { c.heapGoal = c.heapLive + 64<<10 } } else { if c.heapGoal < c.heapLive+1<<20 { c.heapGoal = c.heapLive + 1<<20 } } // CPU 利用率(25%)。 totalUtilizationGoal := float64(procs) * gcBackgroundUtilization // 职业工(dedicated)取整,小数为小时工(fractional)。 c.dedicatedMarkWorkersNeeded = int64(totalUtilizationGoal + 0.5) utilError := float64(c.dedicatedMarkWorkersNeeded)/totalUtilizationGoal - 1 const maxUtilError = 0.3 if utilError < -maxUtilError || utilError > maxUtilError { if float64(c.dedicatedMarkWorkersNeeded) > totalUtilizationGoal { // Too many dedicated workers. c.dedicatedMarkWorkersNeeded-- } c.fractionalUtilizationGoal = (totalUtilizationGoal - float64(c.dedicatedMarkWorkersNeeded)) / float64(procs) } else { c.fractionalUtilizationGoal = 0 } // 当 GODEBUG=gcstoptheword 关闭并发,全部成为正式工。 if debug.gcstoptheworld > 0 { c.dedicatedMarkWorkersNeeded = int64(procs) c.fractionalUtilizationGoal = 0 } // 清除所有 P 相关状态。 for _, p := range allp { p.gcAssistTime = 0 p.gcFractionalMarkTime = 0 } // 计算辅助回收比率。 c.revise() }
随后,解除 STW,让 MP 重回调度循环(schedule)。在这里,检查垃圾回收状态,按需激活 worker.G 开始垃圾标记工作。
// proc.go func schedule() { if gp == nil && gcBlackenEnabled != 0 { gp = gcController.findRunnableGCWorker(_g_.m.p.ptr()) } execute(gp, inheritTime) }
// mgcpacer.go // findRunnableGCWorker returns a background mark worker for _p_ if it // should be run. This must only be called when gcBlackenEnabled != 0. func (c *gcControllerState) findRunnableGCWorker(_p_ *p) *g { // 检查任务队列。 // 刚启动时,gcMarkRootPrepare 准备了 work.markrootJobs 任务。 if !gcMarkWorkAvailable(_p_) { return nil } // 取一个预先创建的 worker G。 node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop()) if node == nil { return nil } // 减法(lock-free)。 decIfPositive := func(ptr *int64) bool { for { v := atomic.Loadint64(ptr) if v <= 0 { return false } if atomic.Casint64(ptr, v, v-1) { return true } } } // 消费工人名额,设置 P.worker 工种。 if decIfPositive(&c.dedicatedMarkWorkersNeeded) { // This P is now dedicated to marking until the end of // the concurrent mark phase. _p_.gcMarkWorkerMode = gcMarkWorkerDedicatedMode } else if c.fractionalUtilizationGoal == 0 { // No need for fractional workers. gcBgMarkWorkerPool.push(&node.node) return nil } else { // Is this P behind on the fractional utilization goal? delta := nanotime() - c.markStartTime if delta > 0 && float64(_p_.gcFractionalMarkTime)/float64(delta) > c.fractionalUtilizationGoal { // Nope. No need to run a fractional worker. gcBgMarkWorkerPool.push(&node.node) return nil } // Run a fractional worker. _p_.gcMarkWorkerMode = gcMarkWorkerFractionalMode } // 修改 worker.G 状态,并返回。 gp := node.gp.ptr() casgstatus(gp, _Gwaiting, _Grunnable) return gp }
其他 MP 调度不到任务时,也尝试以临时工(idle)身份参与回收工作。
// proc.go func findrunnable() (gp *g, inheritTime bool) { // We have nothing to do. // // If we're in the GC mark phase, can safely scan and blacken objects, // and have work to do, run idle-time marking rather than give up the // P. if gcBlackenEnabled != 0 && gcMarkWorkAvailable(_p_) { node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop()) if node != nil { _p_.gcMarkWorkerMode = gcMarkWorkerIdleMode gp := node.gp.ptr() casgstatus(gp, _Gwaiting, _Grunnable) return gp, false } } }
工作
工人醒来后,开始标记存活对象。
// mgc.go func gcBgMarkWorker() { gp := getg() node := new(gcBgMarkWorkerNode) node.gp.set(gp) ... // 在一个周期内,非专业工种会被抢占退出。而后可能再次 // 进入标记工作。所以,该循环是必要的。 for { // 休眠,直到被 findRunnableGCWorker 唤醒。 gopark(func(g *g, nodep unsafe.Pointer) bool { // 将当前 worker G 放回池内。 node := (*gcBgMarkWorkerNode)(nodep) gcBgMarkWorkerPool.push(&node.node) return true }, unsafe.Pointer(node), waitReasonGCWorkerIdle, traceEvGoBlock, 0) // 唤醒后准备工作。 pp := gp.m.p.ptr() startTime := nanotime() pp.gcMarkWorkerStartTime = startTime // 等待人数 -1 。 decnwait := atomic.Xadd(&work.nwait, -1) // 工作! systemstack(func() { casgstatus(gp, _Grunning, _Gwaiting) switch pp.gcMarkWorkerMode { default: throw("gcBgMarkWorker: unexpected gcMarkWorkerMode") case gcMarkWorkerDedicatedMode: gcDrain(&pp.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit) if gp.preempt { // We were preempted. This is // a useful signal to kick // everything out of the run // queue so it can run // somewhere else. if drainQ, n := runqdrain(pp); n > 0 { lock(&sched.lock) globrunqputbatch(&drainQ, int32(n)) unlock(&sched.lock) } } // Go back to draining, this time // without preemption. gcDrain(&pp.gcw, gcDrainFlushBgCredit) case gcMarkWorkerFractionalMode: gcDrain(&pp.gcw, gcDrainFractional|gcDrainUntilPreempt|gcDrainFlushBgCredit) case gcMarkWorkerIdleMode: gcDrain(&pp.gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit) } casgstatus(gp, _Gwaiting, _Grunning) }) // 工作结束,统计时间。 duration := nanotime() - startTime gcController.logWorkTime(pp.gcMarkWorkerMode, duration) if pp.gcMarkWorkerMode == gcMarkWorkerFractionalMode { atomic.Xaddint64(&pp.gcFractionalMarkTime, duration) } // 等待人数 +1,去掉工种。 incnwait := atomic.Xadd(&work.nwait, +1) pp.gcMarkWorkerMode = gcMarkWorkerNotWorker // 如果所有工人都进入等待状态,且没有剩余工作。标记结束! if incnwait == work.nproc && !gcMarkWorkAvailable(nil) { node.m.set(nil) gcMarkDone() } } }
等待总人数是多少,其实不重要。只要 work.nproc == work.nwait,就表示没有人在工作。
// mgc.go // gcBgMarkPrepare sets up state for background marking. func gcBgMarkPrepare() { // Background marking will stop when the work queues are empty // and there are no more workers (note that, since this is // concurrent, this may be a transient state, but mark // termination will clean it up). Between background workers // and assists, we don't really know how many workers there // will be, so we pretend to have an arbitrarily large number // of workers, almost all of which are "waiting". While a // worker is working it decrements nwait. If nproc == nwait, // there are no workers. work.nproc = ^uint32(0) work.nwait = ^uint32(0) }
结束本次回收周期前,需要处理一些额外状况。
// mgc.go // gcMarkDone transitions the GC from mark to mark termination if all // reachable objects have been marked (that is, there are no grey // objects and can be no more in the future). Otherwise, it flushes // all local work to the global queues where it can be discovered by // other workers. func gcMarkDone() { // Ensure only one thread is running the ragged barrier at a time. semacquire(&work.markDoneSema) top: // 再次检查。如任务未完成,则不能终止。 if !(gcphase == _GCmark && work.nwait == work.nproc && !gcMarkWorkAvailable(nil)) { semrelease(&work.markDoneSema) return } semacquire(&worldsema) gcMarkDoneFlushed = 0 systemstack(func() { gp := getg().m.curg casgstatus(gp, _Grunning, _Gwaiting) // 确保所有 P 处理好本地任务。 forEachP(func(_p_ *p) { wbBufFlush1(_p_) _p_.gcw.dispose() if _p_.gcw.flushedWork { atomic.Xadd(&gcMarkDoneFlushed, 1) _p_.gcw.flushedWork = false } }) casgstatus(gp, _Gwaiting, _Grunning) }) if gcMarkDoneFlushed != 0 { // More grey objects were discovered since the // previous termination check, so there may be more // work to do. semrelease(&worldsema) goto top } // 记录结束时间,并再次 STW !!! now := nanotime() work.tMarkTerm = now work.pauseStart = now systemstack(stopTheWorldWithSema) // 禁用工人和辅助。 atomic.Store(&gcBlackenEnabled, 0) // 唤醒被休眠的辅助。 gcWakeAllAssists() semrelease(&work.markDoneSema) schedEnableUser(true) // 本周期结束。 nextTriggerRatio := gcController.endCycle(now, int(gomaxprocs), work.userForced) gcMarkTermination(nextTriggerRatio) }
终止
启动清理任务,通知调度器停止相关工作。
// mgc.go // World must be stopped and mark assists and background workers must be // disabled. func gcMarkTermination(nextTriggerRatio float64) { setGCPhase(_GCmarktermination) // 收尾。 systemstack(func() { gcMark(startTime) }) // 标记结束(禁用写屏障),开启清理。 systemstack(func() { setGCPhase(_GCoff) gcSweep(work.mode) }) // Update GC trigger and pacing for the next cycle ... // Update timing memstats ... // Update work.totaltime ... // Compute overall GC CPU utilization ... // 唤醒等待标记结束的 Gs。(runtime.GC) lock(&work.sweepWaiters.lock) injectglist(&work.sweepWaiters.list) unlock(&work.sweepWaiters.lock) sl := sweep.active.begin() // 解除 STW !!! systemstack(func() { startTheWorldWithSema(true) }) // 回收 workbuf、stackpool、mcache.stackcache、mcache.alloc ... sweep.active.end(sl) if !concurrentSweep { Gosched() } }
因 gcMarkDone gcBlackenEnabled = 0
,调度自然不会再去折腾垃圾回收。
// proc.go func schedule() { if gp == nil && gcBlackenEnabled != 0 { gp = gcController.findRunnableGCWorker(_g_.m.p.ptr()) } } func findrunnable() (gp *g, inheritTime bool) { if gcBlackenEnabled != 0 && gcMarkWorkAvailable(_p_) { node := (*gcBgMarkWorkerNode)(gcBgMarkWorkerPool.pop()) } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论