欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 创投人物 > Golang内存模型与分配机制

Golang内存模型与分配机制

2024/10/24 3:25:06 来源:https://blog.csdn.net/feiqipengcheng/article/details/139708865  浏览:    关键词:Golang内存模型与分配机制

简述

在这里插入图片描述
mheap为堆,堆和进程是一对一的;mcentral(小mheadp),mcahe(GMP的P私有),分配内存顺序由后向前。
在解决这个问题,Golang 在堆 mheap 之上,依次细化粒度,建立了mcentral、mcache 的模型,下面对三者作个梳理:

  • mheap:全局的内存起源,访问要加全局锁
  • mcentral:每种对象大小规格(全局共划分为68种)对应的缓存,锁的粒度也仅限于同一种规格以内
  • mcache:每个P(正是GMP中的P)持有一份的内存缓存,访问时无锁
    在这里插入图片描述
    首先理下page和mspan两个概念∶
    (1) page:最小的存储单元.
    Golang 借鉴操作系统分页管理的思想,每个最小的存储单元也称之为页page,但大小为8 KB
    (2) mspan:最小的管理单元.
    mspan 大小为 page 的整数倍,且8B到80 KB被划分为67种不同的规格,分配对象时,会根据大小映射到不同规格的mspan,从中获取空间.
    于是,我们回头小节多规格mspan下产生的特点︰
  • 根据规格大小,产生了等级的制度
  • 消除了外部碎片,但不可避免会有内部碎片
  • 宏观上能提高整体空间利用率
  • 正是因为有了规格等级的概念,才支持mcentral实现细锁化

核心概念梳理

内存单元mspan

在这里插入图片描述
分点阐述mspan的特质:

  • mspan是Golang内存管理的最小单元
  • mspan大小是page的整数倍(Go 中的page 大小为8KB),且内部的页是连续的(至少在虚拟内存的视角中是这样)
  • 每个mspan根据空间大小以及面向分配对象的大小,会被划分为不同的等级
  • 同等级的mspan 会从属同一个mcentral,最终会被组织成链表,因此带有前后指针((prev、next)·- 由于同等级的mspan内聚于同一个mcentral,所以会基于同一把互斥锁管理
  • mspan 会基于bitMap辅助快速找到空闲内存块(块大小为对应等级下的object大小),此时需要使用到Ctz64算法.
    在这里插入图片描述
    在这里插入图片描述

内存单元等级spanClass

mspan根据空间大小和面向分配对象的大小,被划分为67种等级(1-67,实际上还有一种隐藏的0级,用于处理更大的对象,上不封顶)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
除了上面谈及的根据大小确定的mspan等级外,每个object还有一个重要的属性叫做nocan,标识了object是否包含指针,在gc时是否需要展开标记.
在Golang 中,会将span class + nocan两部分信息组装成一个uint8,形成完整的
spanClass 标识.8个 bit中,高7位表示了上表的span等级((总共67+1个等级,8个bit足够用了),最低位表示nocan信息.
在这里插入图片描述

线程缓存mcache

在这里插入图片描述
(1) mcache是每个Р独有的缓存,因此交互无锁
(2) mcache 将每种spanClass 等级的mspan各缓存了一个,总数为2 (nocan维度)*68(大小维度)=136
(3) mcache 中还有一个为对象分配器tiny allocator,用于处理小于16B对象的内存分配
在这里插入图片描述

中心缓存mcentral

在这里插入图片描述
要点∶
(1)每个mcentral对应一种spanClass
(2)每个mcentral 下聚合了该spanClass 下的mspan
(3) mcentral 下的mspan分为两个链表,分别为有空间mspan链表partial和满空间mspan链表full
(4)每个mcentral一把锁
在这里插入图片描述

全局堆缓存mheap

要点

  • 对于Golang上层应用而言,堆是操作系统虚拟内存的抽象·
  • 以页(8KB)为单位,作为最小内存存储单元
  • 负责将连续页组装成mspan
  • 全局内存基于bitMap标识其使用情况,每个bit对应一页,为0则自由,为1则已被mspan组装·
  • 通过heapArena 聚合页,记录了页到mspan的映射信息
  • 建立空闲页基数树索引 radix tree index,辅助快速寻找空闲页
  • 是mcentral 的持有者,持有所有spanClass 下的mcentral,作为自身的缓存
  • 内存不够时,向操作系统申请,申请单位为heapArena (64M)
    在这里插入图片描述

空闲也索引pageAlloc

数据结构背后的含义:

  • mheap 会基于bitMap标识内存中各页的使用情况,bit位为0代表该页是空闲的,为1代表该页已被mspan占用.
    • 每棵基数树聚合了16 GB内存空间中各页使用情况的索引信息,用于帮助 mheap快速找到指定长度的连续空闲页的所在位置
  • mheap持有214 棵基数树,因此索引全面覆盖到 214*16 GB = 256 T的内存空间.

在这里插入图片描述

  • 每个父 pallocSum有8个子pallocSum
  • 根pallocSum总览全局,映射的 bitMap范围为全局的16 GB空间(其max最大值为221,因此总空间大小为221*8KB=16GB) ;
  • 从首层向下是一个依次八等分的过程,每一个pallocSum映射其父节点bitMap范围的八分之一,因此第二层pallocSum的 bitMap 范围为16GB/8 = 2GB,以此类推,第五层节点的范围为16GB /(84) = 4 MB,已经很小
  • 聚合信息时,自底向上.每个父pallocSum聚合8个子pallocSum的start、max、end信息,形成自己的信息,直到根pallocSum,坐拥全局16 GB的start、max、end 信息
  • mheap寻页时,自顶向下.对于遍历到的每个pallocSum,先看起start 是否符合,是则寻页成功;再看max是否符合,是则进入其下层孩子pallocSum中进一步寻访;最后看end和下一个同辈pallocSum 的start聚合后是否满足,是则寻页成功.

每层节点
在这里插入图片描述
基数树中,每个节点称之为PallocSum,是一个uint64类型,体现了索引的聚合信息,包含以下四部分:

  • start:最右侧21个bit,标识了当前节点映射的 bitMap 范围中首端有多少个连续的0 bit(空闲页),称之为start;
  • max:中间21个bit,标识了当前节点映射的 bitMap范围中最多有多少个连续的0 bit(空闲页),称之为max;
  • end:左侧21个 bit,标识了当前节点映射的 bitMap范围中最末端有多少个连续的0 bit(空闲页),称之为end.
  • 最左侧一个bit,弃置不用
    在这里插入图片描述
    基数树节点
    在这里插入图片描述
    在这里插入图片描述

heapArena

  • 每个heapArena包含8192个页,大小为8192* 8KB = 64 MB
  • **heapArena记录了页到msplan的映射.**因为GC时,通过地址偏移找到页很方便,但找到其所属的mspan不容易.因此需要通过这个映射信息进行辅助.
  • heapArena是mheap向操作系统申请内存的单位(64MB)
    在这里插入图片描述

对象分配流程

下面来串联 Golang中分配对象的流程,不论是以下哪种方式,最终都会殊途同归步入mallocgc方法中,并且根据3.1小节中的策略执行分配流程:

  • new(T)
  • &T{}
  • make(xxxx)

分配流程总览

Golang中,依据 object的大小,会将其分为下述三类∶

  • tiny微对象(0-16B)
  • small小对象(16B-32KB)
  • large大对象(32KB—)

不同类型的对象,会有着不同的分配策略,这些内容在mallocgc方法中都有体现.
核心流程类似于读多级缓存的过程,由上而下,每一步只要成功则直接返回.若失败,则由下层方法兜底.

对于微对象的分配流程︰

(1)从P专属mcache 的tiny 分配器取内存(无锁)
(2)根据所属的spanClass,从Р专属mcache 缓存的mspan中取内存(无锁)
(3)根据所属的spanClass 从对应的mcentral中取 mspan填充到mcache,然后从mspan中取内存(spanclass粒度锁)
(4)根据所属的spanClass,从 mheap 的页分配器 pageAlloc取得足够数量空闲页组装成mspan填充到mcache,然后从mspan中取内存(全局锁)
(5) mheap向操作系统申请内存,更新页分配器的索引信息,然后重复(4) .

对于小对象的分配流程是跳过〔1)步,执行上述流程的(2) - (5)步;

对于大对象的分配流程是跳过(1)-(3)步,执行上述流程的(4) - (5)步.

主干方法,mallocgc

微对象分配
在这里插入图片描述
在这里插入图片描述
小对象分配
在这里插入图片描述
大对象分配
在这里插入图片描述

tiny分配

每个Р独有的mache 会有个微对象分配器,基于 offset线性移动的方式对微对象进行分配,每16B成块,对象依据其大小,会向上取整为2的整数次幂进行空间补齐,然后进入分配流程.
在这里插入图片描述
在这里插入图片描述

mcache分配

在这里插入图片描述
在mspan中,基于Ctz64算法,根据 mspan.allocCache 的 bitMap信息快速检索到空闲的object块,进行返回.
在这里插入图片描述

mcentral分配

当mspan无可用的object内存块时,会步入mcache.nextFree方法进行兜底.
在这里插入图片描述
在这里插入图片描述
mcentral.cacheSpan方法中,会加锁(spanClass 级别的sweepLocker),分别从 partial和full中尝试获取有空间的mspan:
在这里插入图片描述

mheap分配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

操作系统申请

倘若mheap中没有足够多的空闲页了,会发起mmap系统调用,向操作系统申请额外的内存空间.
在这里插入图片描述
在这里插入图片描述

基数树寻页

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com