9张图轻松吃透Go内存管理单元

本文详细介绍了Go语言内存管理的核心——mspan,包括page、object、FreeList和sizeclass等概念。通过图解展示了mspan如何由连续的page组成,以及如何拆分成不同大小的object。FreeList作为一种节省内存的数据结构,用于管理object的分配。此外,文章还讨论了spanclass的概念,它结合sizeclass和mspan的类型标志,用于确定对象大小和垃圾回收策略。最后,文章强调了mspan在内存管理和垃圾回收中的重要性,并预告了接下来将探讨的堆内存分配过程。

导读


本文基于Go源码版本1.16、64位Linux平台、1Page=8KB、本文的内存特指虚拟内存

今日继续更新《Go语言轻松系列》第二章「内存与垃圾回收」第二部分「Go语言内存管理」。

点击查看本系列更多文章

4fc1a779eff78d6527f2aa096c514548.png

想深入了解Go语言的内存管理实现,必然绕不开「Go内存管理单元mspan」,Go堆内存、栈内存的分配过程都依赖了「内存管理单元mspan。今天我们就通过几张图,层层深入并解开「Go内存管理单元mspan」的神秘面纱。

本文包含的具体概念如下:

  • page的概念

  • mspan的概念

  • object的概念

  • FreeList的概念

  • sizeclass的概念

  • spanclass的概念

本文也是为了后续学习Go堆内存、栈内存的分配过程打好基础。

正文


介绍Go内存管理单元mspan前,需要先看下page的概念,因为mspan是由N个连续page组成。

page的概念


操作系统是按page管理内存的,同样Go语言也是也是按page管理内存的,1page为8KB,保证了和操作系统一致,如下图所示:

891e3ce849be6bcd429aaf429f7781e4.png

Go内存管理单元mspan通常由N个连续page组成,如下图所示:

mspanpage组成


7cf89f518263d08594d8b2ce0682c75f.png

  • mspan可以由1个page组成

  • mspan也可以由2个连续的page组成

  • mspan也可以由3个连续的page组成

  • mspan更可以由N个连续的page组成

上图左半部分是mspan的结构体的关键字段,其中npages就代表了这个mspan是由几个连续的page组成。

除此之外,mspanmspan之间还可以构成链表,如下图所示

mspan可构成链表


4a2289c6ec6a834461788f68f3e99793.png

这里需要注意的是:只有npages的值相同的mspan互相才可以组成一个链表。如上图所示,具体原因下文会讲。

看到这里,你会以为Go是按页page8KB为最小单位分配内存的吗?

答案:当然不是,如果这样的话会导致内存使用率不高。Go语言内存管理器会把mspan再拆解为更小粒度的单位object。如下图所示:

b0cec68ae274de3fc026f3678570efad.png

objectobject之间构成一个链表,大家这里肯定会想到是LinkedList,实际上并不是,因为LinkedList节点自身的指针也会占用8B内存,作为内存管理器,这部分内存会被白白浪费掉,所以这里通常使用的数据结构是FreeList

什么是FreeList?


FreeList本质上还是个LinkedList,和LinkedList的区别:

  • FreeList没有Next属性,所以不是用Next属性存放下一个节点的指针的值。

  • FreeList“相当于使用了Value的前8字节”(其实就是整块内存的前8字节)存放下一个节点的指针。

  • 分配出去的节点,节点整块内存空间可以被复写(指针的值可以被覆盖掉)

如下图所示:

64d9330a453b0dfe551e44a9e7cc008e.png

所以:FreeList一个节点最小为8字节Byte

备注:因为要存指针,指针的大小为8字节,为什么?可以参考之前文章《64位平台下,指针自身的大小为什么是8字节?》

得到Go内存管理单元mspan被拆解为object图示如下:

cdb3a9cd0f5fd9ddb8a10da9f93fd91b.png

到这里问题又来了,object的具体大小是多大呢,是怎么决定的?

答案:是由sizeclass决定的。

什么是sizeclass


sizeclass是一个映射列表,实际是一个数组类型[68]uint16,它的值决定了object的大小,除此之外,mspan由几pages构成也是sizeclass值决定的。sizeclass映射列表的具体规则如下:

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

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

字段解释:
class: sizeclass值 
bytes/obj: 该`mspan`拆分object大小
bytes/span: 该`mspan`是由几pages组成
objects: 该`mspan`共计包含的object数量

class  bytes/obj  bytes/span  objects
1          8        8192     1024
2         16        8192      512
3         24        8192      341
4         32        8192      256
5         48        8192      170
6         64        8192      128
...略...
65      27264       81920        3 
66      28672       57344        2
67      32768       32768        1

表格左右滑动查看

sizeclassobject大小由几pages组成
0保留1page
18Byte1pages
216Byte1page
324Byte1page
.........
6732KB4pages

所以mspan结构体上只要维护一个sizeclass的字段,就可以知道该mspanobject的大小、数量。但是呢,实际上这个字段并不是sizeclass,而是spanclass,如下图所示:

73d46208eff978d57b03db43d456a630.png

那么,问题又来了😂。

什么是spanclass


实际上Go内存管理单元mspan被分为了两类:

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

  • 第二类:不需要垃圾回收扫描的mspan,简称noscan

所以说并不是所有的Go内存管理单元mspan会被垃圾回收扫描。为了区别这两类mspan,Go语言把类型标识和上面sizeclass的值一起放在了同一个字段里,具体如下:

  • sizeclass值左移一位:sizeclass << 1

  • sizeclass值最后一位存类型

    • 最后一位为1:则是不需要垃圾回收扫描的mspan

    • 最后一位为0:则是需要垃圾回收扫描的mspan

图示如下:

a7ca2f3b94ebc9ea8dee4f9598b62690.png

总结


mspan拆分object总结


这里我们以spanclass的10进制值为7的mspan为例:

表格左右滑动查看

spanclass10进制值为7
可得,spanclass2进制为0000 0111
可得,sizeclass7>>1:2进制0000 0011,10进制3
可得,mspan由1page组成,共计8KB(8192Byte)
可得,object大小为24Byte
可得,mspan共计包含341个object
可得,mspan尾部浪费8Byte

具体图示如下:

aa0b0abc8d3709cc85b85819a017e86b.png

mspan关键字段总结


挑选mspan的几个重要字段,如下图:

f25e39ca379f456b2600d08ffabbd8b5.png

表格左右滑动查看

字段名解释
next、prev、listmspan之间可以构成链表
startAddrmspan内存的开始位置,N个连续page内存的开始位置
npagesmspan由几page组成
freeindex空闲object链表的开始位置
nelems一共有多少个object
spanclass决定object的大小、以及当前mspan是否需要垃圾回收扫描
......

下篇文章就开始讲解Go堆内存的分配时机与过程。

点击查看本系列更多文章


Go轻松进阶系列 更多文章

点击下方关注我的公众号

ad9616c92ab3c97497fdd157ffb7e5b1.png

38eeddfc13b74ba900f121f534084384.gif

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值