返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

3.3.2 启动

发布于 2024-10-12 19:15:57 字数 6027 浏览 0 评论 0 收藏 0

启动需要确定工作模式,并准备参与标记的工人。除此之外,还要确保前次清理结束。

// 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文