返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.6.4 池

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

与 central 类似,有个全局 stackpool 负责平衡和提供后备内存。

// stack.go

// Global pool of spans that have free stacks.
// Stacks are assigned an order according to size.
//     order = log_2(size/FixedStack)
// There is a free list for each order.

var stackpool [_NumStackOrders]struct {
    item stackpoolItem
}

type stackpoolItem struct {
    mu   mutex
    span mSpanList
}

扩容

当分配操作在 cache 找不到可用内存时,便从 pool 扩容。

// stack.go

// stackcacherefill/stackcacherelease implement a global pool of stack segments.
// The pool is required to prevent unlimited growth of per-thread caches.

func stackcacherefill(c *mcache, order uint8) {

    // Grab some stacks from the global cache.
    // Grab half of the allowed capacity (to prevent thrashing).
    
    var list gclinkptr
    var size uintptr
    
    // 循环提取,直到达到一半上限容量。
    for size < _StackCacheSize/2 {
        x := stackpoolalloc(order)
        x.ptr().next = list
        list = x
        size += _FixedStack << order
    }
    
    c.stackcache[order].list = list
    c.stackcache[order].size = size
}

丛 heap 取 span 后,会将内存分割成多块,构成链表(span.manualFreeList)。

从该链表提取。如果链表为空,则将该 span 从 pool 移除。

// stack.go

// Allocates a stack from the free pool. Must be called with
// stackpoolmu held.

func stackpoolalloc(order uint8) gclinkptr {

    // 从链表找到对应等级的 span。
    list := &stackpool[order].item.span
    s := list.first
    
    // 如果为空,则从堆获取。
    if s == nil {
        
        // 按上限从 heap 取 span。
        s = mheap_.allocManual(_StackCacheSize>>_PageShift, &memstats.stacks_inuse)
        s.elemsize = _FixedStack << order
        
        // 分成多个小块,将地址串成链表。
        for i := uintptr(0); i < _StackCacheSize; i += s.elemsize {
            x := gclinkptr(s.base() + i)
            x.ptr().next = s.manualFreeList
            s.manualFreeList = x
        }
        
        // 将 span 添加到链表。
        list.insert(s)
    }
    
    // 从 span 分出一块,用于返回。
    x := s.manualFreeList
    s.manualFreeList = x.ptr().next
    s.allocCount++
    
    // 如果 span 没有剩余空间,则从链表移除。
    if s.manualFreeList.ptr() == nil {
        list.remove(s)
    }
    
    return x
}

释放

当释放操作发现 cache 里缓存过多,则归还一些给 pool。

// stack.go

func stackcacherelease(c *mcache, order uint8) {
    x := c.stackcache[order].list
    size := c.stackcache[order].size
    
    // 上交,保留一半。
    for size > _StackCacheSize/2 {
        y := x.ptr().next
        stackpoolfree(x, order)
        x = y
        size -= _FixedStack << order
    }
    
    c.stackcache[order].list = x
    c.stackcache[order].size = size
}
// Adds stack x to the free pool. 

func stackpoolfree(x gclinkptr, order uint8) {

    // 根据地址找到对应 span。
    s := spanOfUnchecked(uintptr(x))
    
    // 马上有剩余空间,放回 pool 链表。
    if s.manualFreeList.ptr() == nil {
        stackpool[order].item.span.insert(s)
    }
    
    // 将空间放回 span 内的切分链表。
    x.ptr().next = s.manualFreeList
    s.manualFreeList = x
    s.allocCount--
    
    // 如果 span 收回全部空间,归还给 heap。
    if gcphase == _GCoff && s.allocCount == 0 {
        
        // Span is completely free. Return it to the heap
        // immediately if we're sweeping.
        
        stackpool[order].item.span.remove(s)
        s.manualFreeList = 0
        mheap_.freeManual(s, &memstats.stacks_inuse)
    }
}

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

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

发布评论

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