Go内存管理

Go内存管理

注:本文基于Go源码版本1.16

4.1 基本概念

  • 虚拟内存
通过内存管理单元MMU把虚拟地址转换成物理地址,加载代码,运行指令,然后释放虚拟内存。
从高地址开始,向低地址扩展,内存管理简单,系统自动回收
从低地址开始,向高地址扩展,申请和释放内存需要自己动手。我们所说的go的内存管理,实际是对 堆内存的管理,主要通过runtime来分配和管理

4.2 arena、bitmap、spans

4.2.0

Go在程序启动时,先向操作系统申请一块内存(虚拟地址空间),切成小块后自己管理。

申请到的内存块被分配了三个区域,在X64上分别是512MB,16GB,512GB.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLhTp1LU-1662642817747)(D:\go面试知识\photo\Snipaste_2022-09-08_17-41-54.png)]

4.2.1 arena

arena就是堆,把内存切割成8KB大小的page

4.2.2 bitmap

当从堆中申请内存时,需要知道哪些地址保存了对象。bitmap区域用于标识arena区域哪些地址保存了对象。

用4bit标志位标识对象是否包含指针、4bit标志位标识对象是否被GC标记。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IfLXRia0-1662642817748)(D:\go面试知识\photo\Snipaste_2022-09-08_17-47-57.png)]

bitmap中一个byte大小的内存对应arena区域中4个指针的大小(指针大小为8B)。所以bitmap区域大小为512GB/(4*8B)=16GB

bitmap的高地址部分指向arena区域的低地址部分。bitmap的地址是由高地址向低地址增长的。

4.2.3 spans

spans存放mspan(page组成的内存管理基本单元)的指针,每个指针对应一个page。spans区域的大小为512GB/8KB*8B=512MB。8KB为一个page的大小。8B为一个指针的大小。

创建mspan时,按页填充对应的spans区域,在回收object时,根据地址很容易找到它所属的mspan

4.3 mspan 内存管理单元

针对申请内存的对象的大小不同,把不同数量的page组合在一起,称为mspan。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GgqDSmPq-1662642817748)(D:\go面试知识\photo\Snipaste_2022-09-08_17-58-36.png)]

mspan是一个包含起始地址、mspan规格,page数量等内容的双向链表。

mspan结构体部分字段如下

type mspan struct {
    //链表前向指针,用于将span链接起来
	next *mspan	
	
	//链表前向指针,用于将span链接起来
	prev *mspan
    
    //双向链表
    list *mSpanList
	
	//mspan内存的起始位置
	startAddr uintptr 
	
	//mspan又几个page组成
	npages uintptr 
	
	// 一共有多少个object
	nelems uintptr 
	
    //空闲object链表的开始位置
	freeindex
	
	//决定object的大小,以及当前mspan是否需要垃圾回收扫描
	spanclass spanClass  

}

相同npages数的mspan可用组成一个链表。

mspan会被拆解为更小粒度的单位object,sizeclass决定了object的大小。

sizeclass是一个映射列表,实际是一个数组类型[68]uint16,它的值决定了object的大小。mspan由几个page组成也是sizeclass的值决定的。

go根据class的size,划分了68种mspan。

// 文件位置:`src/runtime/sizeclasses.g`
// 索引0位置被保留使用,具体使用位置后续会讲。

如上文所述,`object`之间采用freelist数据结构构成链表,指针为8Byte所以最小的object大小为8Byte

字段解释:
class: sizeclass值 
bytes/obj: 该`mspan`拆分object大小
bytes/span: 该`mspan`是由几pages组成
objects: 该`mspan`共计包含的object数量
tail waste: 该`mspan`拆分为object之后,mspan剩余末尾浪费的内存

// class  bytes/obj  bytes/span  objects     tail waste  max waste
//
//     1          8        8192     1024           0     87.50%
//     2         16        8192      512           0     43.75%
//     3         24        8192      341           8     29.24%
//     4         32        8192      256           0     21.88%
//     5         48        8192      170          32     31.52%
//     6         64        8192      128           0     23.44%
// 略...
//    62      20480       40960        2           0      6.87%
//    63      21760       65536        3         256      6.25%
//    64      24576       24576        1           0     11.45%
//    65      27264       81920        3         128     10.00%
//    66      28672       57344        2           0      4.91%
//    67      32768       32768        1           0     12.50%

上图列出了67种,还有一种大于32KB的大对象(class=0)从mheap直接分配。

sizeclass的各列解释

1:class的索引,递增
2:object的大小
3:span的大小(span为该种page组合 页数*8KB)
4:可以存储的对象数
5:span%obj的结果
6:最大浪费的内存百分比
  • spanclass

Go的内存管理单元mspan被分为两类

第一类:需要垃圾回收扫描的mspan,简称scan
第二类:不需要垃圾回收扫描的mspan,简称nscan

并不是所有mspan会被垃圾回收扫描,为了区分这两类mspan,Go把类型标识和sizeclass的值放在了同一个字段中,如下:

sizeclass的值左移一位,sizeclass << 1
sizeclass的值最后一位存mspan的类型
	最后一位为1:不需要垃圾回收扫描的mspan
	最后一位为0:需要垃圾回收扫描的mspan

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PHZOiN7W-1662642817749)(D:\go面试知识\photo\Snipaste_2022-09-08_20-26-35.png)]

  • mspan拆分object实例
以spanclass的10进制值为7的mspan为例
7 >> 1 相当于 00000111(7) -> 0000011(3) 7转为2进制后右移一位 得到的结果十进制的值为3
根据sizeclass可得 mspan的page数为1
object大小为24Byte
可得mspan 共计包含 8192/24 = 341.3333... 约等于341个object
mspan尾部浪费8192-24*341=8Byte

4.4 内存管理组件

4.4.1 mcache线程缓存

每个M会绑定一个P,M从P的本地队列中获取一个G,M运行G。某个时间点上M只会运行一个G。

每个P都有个mcache(本地分配器),当申请一个小于32KB的对象时,会优先从本地的mcahche中查找可用的mspan。

由于每个P都有一个mcache且某个时刻M只会运行一个G,当运行G需要mspan时会去对应P的mcache中寻找,不需要加锁,效率高。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zKUsyND9-1662642817749)(D:\go面试知识\photo\Snipaste_2022-09-08_21-08-31.png)]

4.4.2 mcentral中央缓存

实际中央缓存central是一个由136个mcentral类型元素的数组构成。

当mcache中没有找到可用的mspan时,会去向mcentral申请。mcentral会为所有的mcache提供切割好的mspan。mcentral根据sizeclass分为68*2种(scan和nscan),每种mcentral管理一种mspan。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z6w5XRol-1662642817749)(D:\go面试知识\photo\Snipaste_2022-09-08_20-44-52.png)]

lock:因为mcentral是全局的,被所有线程M共享,所以从mcentral获取mspan时要加锁
spanclass:每种mcentral负责的mspan类型
noempty:双端链表,表示还有空闲的mspan可以使用
empty:双端链表,表示这条链表里的mspan都被分配了object,或者是已经被cache取走了的mspan。
  • mcache向mcentral申请mspan的流程
1.根据需要分配内存的对象的大小,找到对应sizeclass,sizeclass经过计算得到spanclass,根据spanclass找到对应的mcentral
2.获取加锁,从noempty的链表种找到一个可用的mspan,并将其从noempty中删除,将取出的mspan加入到empty链表中,mspan返回给工作线程,解锁。
3.归还加锁,将mspan从empty链表删除,将mspan加入到noempty链表中,解锁。
4.4.3 mheap堆缓存

当mcentral也没有可用的mspan时,会向mheap申请,mheap管理的就是一开始申请的堆内存。如果mheap没有足够的内存,会向操作系统申请内存。对于大于32k的对象不会经过mcache、mcentral分配器,而会直接从mheap上申请对应数量的page给应用程序。

4.5 具体分配流程

Go的内存分配器在分配对象时,根据对象的大小分为3类:小对象(obj<=16B)、一半对象(16B<obj<=32B)、大对象(>32B)

大体上的分配流程:

  • 32KB 的对象,直接从mheap上分配;
  • <=16B 的对象使用mcache的tiny分配器分配;
  • (16B,32KB] 的对象,首先计算对象的规格大小,然后使用mcache中相应规格大小的mspan分配;
  • 如果mcache没有相应规格大小的mspan,则向mcentral申请
  • 如果mcentral没有相应规格大小的mspan,则向mheap申请
  • 如果mheap中也没有合适大小的mspan,则向操作系统申请

4.6 tiny分配器

当对象很小时,可能出现以下问题

按照4.5中规则,一个int32类型的变量占4B,应该获取sizeclass=1对应的mspan,也就是每个object是8B.当该变量用掉4B后,还剩4B.对于bool类型的变量占1B,则一个object剩余7B.会造成很多的内碎片。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6UrhnjPz-1662642817750)(D:\go面试知识\photo\Snipaste_2022-09-08_20-58-36.png)]

针对小对象,tiny分配器对于<=16B的内存申请时,会获取sizeclass=2对应的mspan,每个object是16B。多个对象可用复用这个16B的空间,争取把内碎片降到最低(需考虑内存对齐)。

4.7 总结

Go在程序启动时,会向操作系统申请一大块内存,之后自行管理。

Go内存管理的基本单元是mspan,它由若干个页组成,每种mspan可以分配特定大小的object。

mcache, mcentral, mheap是Go内存管理的三大组件,层层递进。mcache管理线程在本地缓存的mspan;mcentral管理全局的mspan供所有线程使用;mheap管理Go的所有动态分配内存。

极小对象会分配在一个object中,以节省资源,使用tiny分配器分配内存;一般小对象通过mspan分配内存;大对象则直接由mheap分配内存。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值