返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

3.3.2 启动

发布于 2024-10-12 19:16:04 字数 3589 浏览 0 评论 0 收藏 0

根据相关条件,分成几种执行方式。

// mgc.go

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

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
    }
    
    // 准备 StopTheWorld。
    semacquire(&worldsema)
    
    // 创建标记工人。
    gcBgMarkStartWorkers()
    
    // 重置标记状态。
    systemstack(gcResetMarkState)
    
    // STW!
    systemstack(stopTheWorldWithSema)
    
    // 确保前次清理结束。
    systemstack(func() {
        finishsweep_m()
    })
    
    // 清除 sync.Pool、sudog 等缓存。
    clearpools()
    
    // 累加周期计数,准备工作。
    work.cycles++
    
    // 控制器本次周期开始。
    gcController.startCycle()
    work.heapGoal = memstats.next_gc
    
    // 进入并发标记阶段。(写屏障启用)
    setGCPhase(_GCmark)
    
    // 准备根对象等。
    gcBgMarkPrepare()
    gcMarkRootPrepare()
    
    // 微小对象分配块被 cache 持有,所以直接加入灰色队列。
    gcMarkTinyAllocs()
    
    // 允许黑化标记。(辅助回收启用)
    atomic.Store(&gcBlackenEnabled, 1)
    
    // 解除 STW,以便 schedule 开启 Worker 进行回收作业。
    systemstack(func() {
        now = startTheWorldWithSema()
    })
    
    // 非并发模式下,当前 goroutine 将被暂停。
    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
}

gcBgMarkStartWorkers

为每个 P 创建一个专职标记的工人(worker goroutine)。

仅表示该 P 可以具备回收能力,是否参加要看 startCycle 里的数量设置。

// mgc.go

// gcBgMarkStartWorkers prepares background mark worker goroutines.
// These goroutines will not run until the mark phase, ...

func gcBgMarkStartWorkers() {
    for _, p := range allp {
        if p.gcBgMarkWorker == 0 {
            
            go gcBgMarkWorker(p)
            
            // 等新工人准备好,再创建下一个。
            notetsleepg(&work.bgMarkReady, -1)
            noteclear(&work.bgMarkReady)
        }
    }
}
func gcBgMarkWorker(_p_ *p) {
    
    park.attach.set(_p_)
    
    // 准备完毕,可以创建下一个。
    notewakeup(&work.bgMarkReady)
    
    for {
        // 
        // gopark 执行 fn,如果函数返回值为 true 则休眠。
        gopark(func(g *g, parkp unsafe.Pointer) bool {
            
            park := (*parkInfo)(parkp)
            if park.attach != 0 {
                
                // 将工人(worker goroutine)关联到 P。
                p := park.attach.ptr()
                park.attach.set(nil)
                if !p.gcBgMarkWorker.cas(0, guintptr(unsafe.Pointer(g))) {
                    return false
                }
            }
            
            return true
        }, unsafe.Pointer(park), ...)
        
        ... 执行标记任务 ...
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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