返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.6.6 垃圾回收

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

在垃圾标记的 markroot 里,引发栈内存回收操作。

// mgcmark.go

func markroot(gcw *gcWork, i uint32) {
    flushmcache(int(i - baseFlushCache))
    systemstack(markrootFreeGStacks)
}

函数 flushmcache 除收回用于分配的内存外,还会清理栈缓存。

// mstats.go

func flushmcache(i int) {
    stackcache_clear(c)
}

将 cache 里保存的 stack 内存全部交回 pool。

如 span 收回全部空间,则返还给 heap。

// stack.go

func stackcache_clear(c *mcache) {
    
    for order := uint8(0); order < _NumStackOrders; order++ {
        x := c.stackcache[order].list
        for x.ptr() != nil {
            y := x.ptr().next
            stackpoolfree(x, order)
            x = y
        }
        
        c.stackcache[order].list = 0
        c.stackcache[order].size = 0
    }
}

而 markrootFreeGStacks 则回收 dead G 所持有栈内存。

// mgcmark.go

// markrootFreeGStacks frees stacks of dead Gs.

func markrootFreeGStacks() {
 
    // Take list of dead Gs with stacks.
    
    list := sched.gFree.stack
    sched.gFree.stack = gList{}
    
    if list.empty() {
        return
    }
    
    // Free stacks.
    q := gQueue{list.head, list.head}
    for gp := list.head.ptr(); gp != nil; gp = gp.schedlink.ptr() {
        stackfree(gp.stack)
        gp.stack.lo = 0
        gp.stack.hi = 0
        
        q.tail.set(gp)
    }
    
    // Put Gs back on the free list.
    sched.gFree.noStack.pushAll(q)
}

在 markroot 对 Grunnable、Gsyscall、Gwaiting G.stack 扫描时,也会进行栈内存收缩。

markroot -> scanstack -> shrinkstack。

// stack.go

func shrinkstack(gp *g) {
    
    oldsize := gp.stack.hi - gp.stack.lo
    newsize := oldsize / 2
    
    // Don't shrink the allocation below the minimum-sized stack allocation.
    if newsize < _FixedStack {
        return
    }
    
    // 通过 SP 位置计算已使用空间。
    // 如果不足 1/4,则收缩栈空间。
    avail := gp.stack.hi - gp.stack.lo
    if used := gp.stack.hi - gp.sched.sp + _StackLimit; used >= avail / 4 {
        return
    }
    
    copystack(gp, newsize)
}

到垃圾扫描结束阶段,会进一步清理栈相关缓存。

// mgc.go

func gcMarkTermination(nextTriggerRatio float64) {
    // Free stack spans. This must be done between GC cycles.
    systemstack(freeStackSpans)
}
// stack.go

// freeStackSpans frees unused stack spans at the end of GC.

func freeStackSpans() {
 
    // 清理 pool 里的内存。
    for order := range stackpool {
        list := &stackpool[order].item.span
        for s := list.first; s != nil; {
            next := s.next
            
            // 如果收回全部空间,归还给 heap。
            if s.allocCount == 0 {
                list.remove(s)
                s.manualFreeList = 0
                mheap_.freeManual(s, &memstats.stacks_inuse)
            }
            
            s = next
        }
    }
    
    // 清理大块缓存。
    for i := range stackLarge.free {
        for s := stackLarge.free[i].first; s != nil; {
            next := s.next
            
            stackLarge.free[i].remove(s)
            mheap_.freeManual(s, &memstats.stacks_inuse)
            
            s = next
        }
    }
}

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

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

发布评论

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