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

导读


本文基于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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux使用虚拟内存管理机制来对物理内存进行管理。通过虚拟内存管理,Linux欺骗用户程序,使每个程序都有4GB的虚拟内存寻址空。这种管理机制使用了一些数据结构来抽象内存管理操作,例如分配和释放内存。其中,用户空间内存数据结构用于管理用户空间的虚拟内存区域。对于内核管理系统中的物理内存,Linux使用以页为最小分配单位的方式进行管理,这样可以更方便地管理物理内存。虽然每页的大小通常比实际内存使用的内存要大,但对于一些行为所需的内存,例如文件描述符、进程描述符和虚拟内存描述符,它们的内存占用量很小,甚至不到一页的大小。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [万字长文,别再说你不懂Linux内存管理了,30 张图给你安排的明明白白](https://blog.csdn.net/itcodexy/article/details/109574799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [【Linux】内存管理机制](https://blog.csdn.net/weixin_46401837/article/details/122497714)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值