Go内存管理 | 青训营笔记

82 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第五篇笔记。

Go的内存是自动管理的不需要考虑变量什么时候申请和释放,因此我们可以更多地投入编程中。

程序申请内存空间的时候会有如下的情况:

  • 系统调用会导致进入内核态,内存分配完毕之后再进入用户态。
  • 频繁申请很小的内存空间,容易出现大量内存碎片,增大操作系统整理碎片的压力,
  • 为了保证内存访问有良好的局部性,开发者需要投入很大的精力去做优化。

因此有了对象池。Golang中的内存管理本质上就是一个内存池。

mheadp

Golang会在程序启动之初。一次性从操作系统哪里申请一大块内存作为内存池。这块内存池会存放在一个叫做mheap的struct中管理,mheap负责将其划分为不同的区域,分配给用户使用。

Golang中的内存存在一下几个概念:

  • page:一块8K大小的内存空间,Go于操作系统之间的内存申请和释放,都是以page为单位的。

  • span:内存块,一个或多个连续的page组成一个span。

  • sizeclass: 空间规格,每一个span都有一个sizeclass,标记着该span中的page应该如何使用。

  • object:对象,用来存储一个变量数据内存空间,一个span在初始化的时候会被划分为一大堆的object。object的数量就是span大小/object大小。内存分配就是把一个个的object分配出去。

内存管理

先来看一张图:

微信截图_20220513161730.png

mcentral

用途相同的span会以链表的形式组织在一起,这里的用途用sizeclass表示。

找到合适的span之后,这些span会被放在一个叫做mcentral中来管理。需要注意的是mcentral结构中有一个lock字段,这是用于并发的情况下如果有协程同时申请内存的话需要使用锁来解决冲突。但锁是低效的。

mcache

所以就有了mcache。每一个处理器对应着一个mcache,当协程申请内存分配的时候,会先从mcache中申请分配,如果cache中没有span可用,再从mcentral中获取,并填充到mcache中。

从mcache上分配内存是不需要加锁的,因为同一时间里,一个处理器只有一个协程在上面运行,不会有竞争的现象。

模型大致如下:

微信截图_20220513161703.png