返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

2.5.2 异步释放

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

定时释放由独立的 goroutine 执行。

// mgc.go

func gcenable() {
    go bgscavenge(c)
}
// proc.go

// The main goroutine.
func main() {
    gcenable()
}

不仅考虑释放总量,还兼顾操作频率带来的开销。

相比 5 分钟闲置更积极,且比 1.13 更细腻。

该 G 以循环方式执行,单次释放足量内存。如释放未果,表示当前没有 “多余” 物理内存,阻塞后等待手工唤醒,否则以定时器唤醒。

// mgcscavenge.go

func bgscavenge(c chan int) {
    
    scavenge.g = getg()
    scavenge.parked = true
    
    // 定时器,唤醒下面循环里的 sleep 操作。
    scavenge.timer = new(timer)
    scavenge.timer.f = func(_ interface{}, _ uintptr) {
        wakeScavenger()
    }
    
    // 阻塞。
    goparkunlock(&scavenge.lock, waitReasonGCScavengeWait, traceEvGoBlock, 1)
    
    // 按页循环释放。
    for {
        systemstack(func() {
            
            // 条件阈值。
            retained, goal := heapRetained(), mheap_.scavengeGoal
            if retained <= goal {
                return
            }
            
            // 单次释放足够物理内存,计算耗时。
            start := nanotime()
            released = mheap_.pages.scavenge(physPageSize, true)
            crit = float64(nanotime() - start)
        })
        
        // 没能释放内存,阻塞。(手工唤醒)
        if released == 0 {
            scavenge.parked = true
            goparkunlock(&scavenge.lock, waitReasonGCScavengeWait, traceEvGoBlock, 1)
            continue
        }
        
        // 计算并调整定时器间隔。
        crit *= 1 + scavengeCostRatio
        adjust := scavengeEWMA / idealFraction
        sleepTime := int64(adjust * crit / (scavengePercent / 100.0))
        
        // 释放足量内存,休眠。(定时器唤醒)
        slept := scavengeSleep(sleepTime)
        
        ...
    }
}

休眠和唤醒操作。

sysmon、finishsweep_m 会执行手工唤醒操作。

func scavengeSleep(ns int64) int64 {
    start := nanotime()
    resetTimer(scavenge.timer, start+ns)
    
    scavenge.parked = true
    goparkunlock(&scavenge.lock, waitReasonSleep, traceEvGoSleep, 2)
    
    return nanotime() - start
}
func wakeScavenger() {
    if scavenge.parked {
        stopTimer(scavenge.timer)
        scavenge.parked = false
        
        // 将 scavenge.g 放回待运行队列。
        var list gList
        list.push(scavenge.g)
        injectglist(&list)
    }
}

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

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

发布评论

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