概述

看下这个调用图


digraph cacheSpan {
    rankdir=TB;
    node[shape=box];

    start[label="开始"];
    end[label="结束"];

    sg[label="获取当前 sweepgen 值"];

    nonempty[label="遍历非空 span 列表"];

    sweepgen[label="如果内存单元的 sweepgen 等于 mheap.sweepgen - 2, 那么意味着当前单元需要清理 \n如果等于 mheap.sweepgen - 1,那么当前管理单元就正在清理";shape=plain; ]

    empty[label="遍历空 span 列表"];

    sweep[label="清理 span 并将其添加到空 span 列表"];
    free[label="释放部分空间"];
    grow[label="创建新的 span 并添加到空 span 列表"];
    cache[label="重新填充 span 的分配缓存"];

    have[label="返回 span 指针"];



    // 处理mspan
	subgraph cluster_span {
		style = dotted
		margin = 10
		"块(span)" [shape = plaintext;fontsize = 24]

        "mheap.alloc_m"
        "notice1" [shape=plain;label="在这里才设置mspan中的spanclass"] 
        {rank=same; "notice1" -> "mheap.alloc_m"[dir=none]; }
	}

    start -> sg;

    {rank=same; sweepgen -> nonempty[style=invis];}

    sg -> nonempty -> sweep -> empty -> free -> have;


    nonempty -> empty;
    empty -> grow -> cache -> have;

    have->end;

}

mcentral中有六个mspan列表,分别是:

  • 有空闲空间
    • 尚未清扫
    • 正在清扫
    • 已经清扫
  • 没有空闲空间
    • 尚未清扫
    • 正在清扫
    • 已经清扫 当需要从mcentral中取走一个mspan时,优先级如上述,不过,不会从最后一种(已经清扫且没有空闲空间)mspan的列表中取mspan。

cacheSpan #


// Allocate a span to use in an mcache.
func (c *mcentral) cacheSpan() *mspan {
    //...
	sg := mheap_.sweepgen
retry:
	var s *mspan
	for s = c.nonempty.first; s != nil; s = s.next { // 非空mspan列表
		// 如果内存单元的 sweepgen 等于 mheap.sweepgen - 2, 那么意味着当前单元需要清理
		// 如果等于 mheap.sweepgen - 1,那么当前管理单元就正在清理
		if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) {
			c.nonempty.remove(s)
			c.empty.insertBack(s)
			unlock(&c.lock)
			s.sweep(true)
			goto havespan
		}
		if s.sweepgen == sg-1 {
			// the span is being swept by background sweeper, skip
			continue
		}
		// we have a nonempty span that does not require sweeping, allocate from it
		c.nonempty.remove(s)
		c.empty.insertBack(s)
		unlock(&c.lock)
		goto havespan
	}

	for s = c.empty.first; s != nil; s = s.next {
		if s.sweepgen == sg-2 && atomic.Cas(&s.sweepgen, sg-2, sg-1) { // 需要清理,然后就先看下是否有空闲的空间。
			// we have an empty span that requires sweeping,
			// sweep it and see if we can free some space in it
			c.empty.remove(s)
			// swept spans are at the end of the list
			c.empty.insertBack(s)
			unlock(&c.lock)
			s.sweep(true)
			freeIndex := s.nextFreeIndex()
			if freeIndex != s.nelems {
				s.freeindex = freeIndex
				goto havespan
			}
			lock(&c.lock)
			// the span is still empty after sweep
			// it is already in the empty list, so just retry
			goto retry
		}
		if s.sweepgen == sg-1 {
			// the span is being swept by background sweeper, skip
			continue
		}
		// already swept empty span,
		// all subsequent ones must also be either swept or in process of sweeping
		break
	}
	unlock(&c.lock)

	// Replenish central list if empty.
	s = c.grow()
	if s == nil {
		return nil
	}
	lock(&c.lock)
	c.empty.insertBack(s)
	unlock(&c.lock)

	// At this point s is a non-empty span, queued at the end of the empty list,
	// c is unlocked.
havespan:
	n := int(s.nelems) - int(s.allocCount)
	if n == 0 || s.freeindex == s.nelems || uintptr(s.allocCount) == s.nelems {
		throw("span has no free objects")
	}
	// Assume all objects from this span will be allocated in the
	// mcache. If it gets uncached, we'll adjust this.
	atomic.Xadd64(&c.nmalloc, int64(n))
	usedBytes := uintptr(s.allocCount) * s.elemsize
	atomic.Xadd64(&memstats.heap_live, int64(spanBytes)-int64(usedBytes))

	freeByteBase := s.freeindex &^ (64 - 1)
	whichByte := freeByteBase / 8
	s.refillAllocCache(whichByte)

    //调整allocCache,使s.freeindex对应于s.allocCache。
    s.allocCache >>= s.freeindex % 64

    return s
}