Golang内存分配器

Golang内存分配器

Go的内存分配器用途: 主要解决小对象的内存分配管理和多线程的内存分配问题. Go语言内存分配算法设计思路主要来源于Google自家的TCMalloc, 有兴趣的可以了解一下

小对象

Go中将大小小于等于32K的对象视作小对象, 大于32K的视作大对象. 大对象是直接分配在堆(Heap)上的, 但是实际应用中小对象的分配是比较频繁的, 所以需要特殊化的管理

32K应该是个经验值

分配过程

小对象分配时, 内存分配器查询Cache组件是否有空闲内存, 有的话直接将内存返回给小对象使用. 没有空闲内存(也就是我们常说的Cache不命中)的话Cache组件会向Central区申请一批小对象内存, 并将此内存缓存起来, 然后返回一个小对象需要的内存大小的内存地址给小对象. 小对象使用完事释放之后的内存是由Cache组件缓存起来的.

Cache组件: Cache组件可能同事存在多个. 我们所知道的Go调度模型MPG, 每个实际的操做系统线程就有一个Cache组件

这里的Central也是一个组件, 它是所有线程共享而某个线程非独占的, 因此向Central申请的时候是需要加锁操做的. 我们需要知道的是, Central组件也是一个缓存, 只不过它缓存的是一组一组的内存page(每个page大小为4k, 也就是4096bit)

那如果Central也没有缓存的空闲内存呢? 那就会从堆(Heap)中申请内存来填充Central. 当然, 对Heap的操做也是需要加锁的, 所有的线程都是共享同一个Heap的. 如果Heap中也没有空闲内存的话, 那就直接从操做系统取内存了

由于小对象内存分配走了应用Cache而不用每次都向操做系统申请内存, 且每个线程对应有个Cache组件,这样一来从Cache查询/获取空闲内存的时候就不用加锁了. 实际上我们在写程序的时候,绝大多数的内存申请都是小于32k的(属于小对象),因此这样的内存分配全部走线程本地cache,不用向操作系统申请显然是非常高效的

引用文章


最后修改于 2023-10-11