七叶笔记 » golang编程 » Golang内存分配机制

Golang内存分配机制

Golang内存分配机制

Go的内存分配分为微内存分配,小内存分配,大内存分配,微内存为小于16字节的内存分配,小内存则为大于16字节小于32KB的内存分配,大内存是大于32KB的内存分配。

Page与Span

在Golang中一个 内存页 为8K,其为Golang内存分配器以此为倍数单位大小去申请,比如申请16G。Span为一个或者多个连续的内存页组成,可以想象成如下结构:

小内存分配

mCache

Go的内存分配采用的类似TCMalloc一样的分配策略,就是对需要分配的内存按照尺寸大小进行分级,按照不同的SizeClass分为<=8KB,<=16KB,<=32KB。

一个Goroutine去申请内存时,就是在其对应的P的mCache中去申请,申请的基本单元称为mSpan,根据mSpan的大小找到最接近其的内存块返回即可。因为其实在P内部去分配的,所有过程中不需要加全局锁,性能会快很多。

mCentral

当然mCache内存块也会分配光,所以它会向mCentral去要,mCentral有两个双向链表组成,一个用来可以被用来分配的内存块组(Non Empty List),另外一个用来存放已经分配出去的内存块组(Empty List),但是mCentral分配和释放的时候是需要加全局锁的,因为它也是所有P共享的。比如分配的流程是这样的:

  1. 获得全局锁GL
  2. 在Non Empty List中获得一块内存
  3. 在Empty List记录下这块内存
  4. 释放全局锁GL

mHeap

当mCentral也没有多余的内存的时候,就需要向mHeap进行申请,而mHeap中存放的是未被切割成一块一块的大的内存块,其称为Arena内存块,在mHeap中以二维数组的形式进行存放。

微内存分配

对于很多频繁申请并且尺寸很小的 内存需求我们叫Tiny对象,其如果每次都是申请然后马上释放,这样会造成空间浪费,所以Golang对这块做了一些优化。想小字符串,或者临时的逃逸变量等等,都会按照微内存来分配,用完之后该空间还能继续使用。

可以把很多Tiny对象打包成一个大对象,然后统一分配一个小内存给该区域就可以。

大内存分配

大内存指的就是大于32Kb的内存,这部分内存分配频率不会太高,但是尺寸可能往往很大,所以直接让mHeap去分配,如果mHeap中的Arena内存块不够用,再向操作系统申请虚拟内存即可。

总结

Golang内存分配主要思想还是按照不同的尺寸去获取最适合的内存块,和Memcached的Slab内存分配策略有一些像,主要目的就是减少内存碎片。

相关文章