上卷 程序设计
中卷 标准库
- 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.3.2 启动
启动需要确定工作模式,并准备参与标记的工人。除此之外,还要确保前次清理结束。
// mgc.go 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) )
// mgc.go // gcStart starts the GC. It transitions from _GCoff to _GCmark (if // debug.gcstoptheworld == 0) or performs all of GC (if // debug.gcstoptheworld != 0). func gcStart(trigger gcTrigger) { // 检查启动条件,并完成前次清理工作。 for trigger.test() && sweepone() != ^uintptr(0) { sweep.nbgsweep++ } // 可能有多个线程检查到触发条件。 // 加锁,并再次执行条件检查,使多余的触发提前退出。 semacquire(&work.startSema) if !trigger.test() { semrelease(&work.startSema) return } // 是否手工 runtime.GC 调用。 work.userForced = trigger.kind == gcTriggerCycle // 根据环境变量 GODEBUG=gcstoptheword 确定回收模式。 mode := gcBackgroundMode if debug.gcstoptheworld == 1 { mode = gcForceMode } else if debug.gcstoptheworld == 2 { mode = gcForceBlockMode } // 准备 STW !!! semacquire(&gcsema) semacquire(&worldsema) // 创建标记工人(mark goroutines)。 gcBgMarkStartWorkers() // 重置标记状态。 systemstack(gcResetMarkState) work.stwprocs, work.maxprocs = gomaxprocs, gomaxprocs if work.stwprocs > ncpu { // This is used to compute CPU time of the STW phases, // so it can't be more than ncpu, even if GOMAXPROCS is. work.stwprocs = ncpu } work.heap0 = atomic.Load64(&gcController.heapLive) work.pauseNS = 0 // 本周期 STW 暂停时间总计。 work.mode = mode // 回收模式。 now := nanotime() work.tSweepTerm = now // 前次清理结束时间。 work.pauseStart = now // 本次 STW 开启时间。 // STW !!! systemstack(stopTheWorldWithSema) // 确保清理结束。 systemstack(func() { finishsweep_m() }) // 清除 sync.Pool、sudog 等缓存。 clearpools() // 周期计数。 work.cycles++ // 控制器本次周期开始。 gcController.startCycle(now, int(gomaxprocs)) work.heapGoal = gcController.heapGoal // In STW mode, disable scheduling of user Gs. This may also // disable scheduling of this goroutine, so it may block as // soon as we start the world again. if mode != gcBackgroundMode { schedEnableUser(false) } // 进入并发标记阶段。(启用写屏障) setGCPhase(_GCmark) // 准备相关数据。 gcBgMarkPrepare() gcMarkRootPrepare() // 微小对象分配块被 cache 持有,所以直接加入灰色队列。 gcMarkTinyAllocs() // 允许黑化标记。(启用辅助回收) atomic.Store(&gcBlackenEnabled, 1) // 解除 STW,以便 schedule 调度标记工人进行回收作业。 systemstack(func() { now = startTheWorldWithSema(trace.enabled) // !!! work.pauseNS += now - work.pauseStart // 累加 STW 暂停时间。 work.tMark = now // 标记开始时间。 }) semrelease(&worldsema) // 非并发模式下,当前 G 将被暂停。 if mode != gcBackgroundMode { Gosched() } // 释放锁,使得其他线程可以进入。 semrelease(&work.startSema) }
设置阶段状态,按需启用或禁止写屏障。
// mgc.go func setGCPhase(x uint32) { atomic.Store(&gcphase, x) writeBarrier.needed = gcphase == _GCmark || gcphase == _GCmarktermination writeBarrier.enabled = writeBarrier.needed || writeBarrier.cgo }
标记工人
按参与方式不同,可将标记工人分为几类:
- 正式工(gcMarkWorkerDedicatedMode):专职清理工作,不会被抢占。
- 小时工(gcMarkWorkerFractionalMode):参与少量工作,可被抢占。
- 临时工(gcMarkWorkerIdleMode):找不到其他任务,临时参与工作,可被抢占。
// mgc.go type gcMarkWorkerMode int const ( gcMarkWorkerNotWorker gcMarkWorkerMode = iota // gcMarkWorkerDedicatedMode indicates that the P of a mark // worker is dedicated to running that mark worker. The mark // worker should run without preemption. gcMarkWorkerDedicatedMode // gcMarkWorkerFractionalMode indicates that a P is currently // running the "fractional" mark worker. The fractional worker // is necessary when GOMAXPROCS*gcBackgroundUtilization is not // an integer and using only dedicated workers would result in // utilization too far from the target of gcBackgroundUtilization. // The fractional worker should run until it is preempted and // will be scheduled to pick up the fractional part of // GOMAXPROCS*gcBackgroundUtilization. gcMarkWorkerFractionalMode // gcMarkWorkerIdleMode indicates that a P is running the mark // worker because it has nothing else to do. The idle worker // should run until it is preempted and account its time // against gcController.idleMarkTime. gcMarkWorkerIdleMode )
标记未开始前,准备好与 P 等数的 worker G。待后续 gcController.startCycle 决定人数和工种。
// mgc.go // gcBgMarkStartWorkers prepares background mark worker goroutines. func gcBgMarkStartWorkers() { // Background marking is performed by per-P G's. Ensure that each P has // a background GC G. for gcBgMarkWorkerCount < gomaxprocs { // 创建 worker G。 go gcBgMarkWorker() // 休眠,等当前这个 worker 准备好再创建下一个。 notetsleepg(&work.bgMarkReady, -1) noteclear(&work.bgMarkReady) // The worker is now guaranteed to be added to the pool before // its P's next findRunnableGCWorker. gcBgMarkWorkerCount++ } }
新建的 worker G 保存在池内,休眠待用。
// mgc.go, runtime2.go // gcBgMarkWorker is an entry in the gcBgMarkWorkerPool. It points to a single // gcBgMarkWorker goroutine. type gcBgMarkWorkerNode struct { // Unused workers are managed in a lock-free stack. node lfnode // The g of this worker. gp guintptr } // Pool of GC parked background workers. Entries are type *gcBgMarkWorkerNode. var gcBgMarkWorkerPool lfstack
// mgc.go func gcBgMarkWorker() { gp := getg() node := new(gcBgMarkWorkerNode) node.gp.set(gp) // 唤醒上面 gcBgMarkStartWorkers 函数,创建下一个 worker G。 notewakeup(&work.bgMarkReady) for { // 休眠,直到被 gcController.findRunnableGCWorker 唤醒开始工作。 gopark(func(g *g, nodep unsafe.Pointer) bool { // 放入池内,供 findRunnableGCWorker 调用。 node := (*gcBgMarkWorkerNode)(nodep) gcBgMarkWorkerPool.push(&node.node) return true }, unsafe.Pointer(node), waitReasonGCWorkerIdle, traceEvGoBlock, 0) // ... 标记工作细节 ... } }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论