看下这个调用图
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
}