Golang中的内存组件关系如下图所示

在学习golang 内存时,经常会涉及几个重要的数据结构,如果不熟悉它们的情况下,理解起来就显得格外的吃力,所以本篇主要对相关的几个内存组件做下数据结构的介绍。
在 Golang 中,mcache
、mspan
、mcentral
和 mheap
是内存管理的四大组件,mcache
管理线程在本地缓存的 mspan
,而 mcentral
管理着全局的 mspan
为所有 mcache
提供所有线程。
根据分配对象的大小,内部会使用不同的内存分配机制,详细参考函数 mallocgo()
<16KB
会使用微小对象内存分配器从P
中的mcache
分配,主要使用mcache.tinyXXX
这类的字段16-32KB
从P
中的mcache
中分配>32KB
直接从mheap
中分配
对于golang中的内存申请流程,大家应该都非常熟悉了,这里不再进行详细描述。

mcache
在GPM关系中,会在每个 P
下都有一个 mcache
字段,用来表示内存信息。
在 Go 1.2 版本前调度器使用的是 GM
模型,将 mcache
放在了 M
里,但发现存在诸多问题,期中对于内存这一块存在着巨大的浪费。每个 M
都持有 mcache
和 stack alloc
,但只有在 M
运行 Go 代码时才需要使用的内存(每个 mcache 可以高达2mb),当 M
在处于 syscall
或 网络请求
的时候是不需要的,再加上 M
又是允许创建多个的,这就造成了很大的浪费。所以从go 1.3版本开始使用了GPM模型,这样在高并发状态下,每个G只有在运行的时候才会使用到内存,而每个 G 会绑定一个P,所以它们在运行只占用一份 mcache,对于 mcache 的数量就是P 的数量,同时并发访问时也不会产生锁。
对于 GM 模型除了上面提供到内存浪费的问题,还有其它问题,如单一全局锁sched.Lock、goroutine 传递问题和内存局部性等。
在 P
中,一个 mcache
除了可以用来缓存小对象外,还包含一些本地分配统计信息。由于在每个P下面都存在一个mcache
,所以多个 goroutine
并发请求内存时是无锁的。