返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.6.4 池

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

与 mcentral 类似,有个全局 stackpool 负责平衡,为 mcache.stackcache 提供后备内存。

// 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
}
// mheap.go

// mSpanList heads a linked list of spans.
type mSpanList struct {
	first *mspan
	last  *mspan
}

type mspan struct {
    manualFreeList gclinkptr // list of free objects in mSpanManual spans    
}
  • stackpoolItem.span: 多个 mspan 构成的链表。
  • mspan.manualFreeList: 切分好可供复用的栈内存。

本地缓存不足,从 stackpool 扩容。

// 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
	lock(&stackpool[order].item.mu)
    
	for size < _StackCacheSize/2 {
		x := stackpoolalloc(order)
		x.ptr().next = list
		list = x
		size += _FixedStack << order
	}
    
	unlock(&stackpool[order].item.mu)
	c.stackcache[order].list = list
	c.stackcache[order].size = size
}
// Allocates a stack from the free pool. Must be called with
// stackpool[order].item.mu held.

func stackpoolalloc(order uint8) gclinkptr {
    
    // 从链表提取 span。
	list := &stackpool[order].item.span
	s := list.first

    // 如 span 为空。
	if s == nil {
        
		// 从堆获取新内存块。
		s = mheap_.allocManual(_StackCacheSize>>_PageShift, spanAllocStack)
		osStackAlloc(s)
        
        // 切分,串成链表。
		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)
	}
    
    // 提取内存块。
	x := s.manualFreeList
	s.manualFreeList = x.ptr().next
	s.allocCount++
    
    // 该 span 没有剩余内存,从链表移除。
	if s.manualFreeList.ptr() == nil {
		// all stacks in s are allocated.
		list.remove(s)
	}
    
	return x
}

而当本地数量过多时,则归还部分给全局池。

// stack.go

func stackcacherelease(c *mcache, order uint8) {
	
    x := c.stackcache[order].list
	size := c.stackcache[order].size
	lock(&stackpool[order].item.mu)
    
	for size > _StackCacheSize/2 {
		y := x.ptr().next
		stackpoolfree(x, order)
		x = y
		size -= _FixedStack << order
	}
    
	unlock(&stackpool[order].item.mu)
	c.stackcache[order].list = x
	c.stackcache[order].size = size
}
// Adds stack x to the free pool. 
// Must be called with stackpool[order].item.mu held.

func stackpoolfree(x gclinkptr, order uint8) {

    // 所在 span。
    s := spanOfUnchecked(uintptr(x))
    
    // 如该 span 内存块链表为空,直接放回。
	if s.manualFreeList.ptr() == nil {
		// s will now have a free stack
		stackpool[order].item.span.insert(s)
	}
    
    // 放回 span 内存块链表。
	x.ptr().next = s.manualFreeList
	s.manualFreeList = x
	s.allocCount--
    
    // 如果该 span 内存被全部收回,则将其归还给堆。
	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
        
		osStackFree(s)
		mheap_.freeManual(s, spanAllocStack)
	}
}

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

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

发布评论

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