Golang内存模型总结1(mspan、mcache、mcentral、mheap)

news2025/2/24 23:06:13

1.内存模型

1.1 操作系统存储模型

从上到下分别是寄存器、高速缓存、内存、磁盘,其中越往上速度越快,空间越小,价格越高。

关键词是多级模型和动态切换

1.2 虚拟内存与物理内存

虚拟内存是一种内存管理技术,允许计算机使用比物理内存更多的内存资源,通过将部分内存存储在硬盘上的方式,扩展了系统的可用内存。

1.3 分页管理

操作系统会把虚拟内存和物理内存切割成固定的尺寸,于虚拟内存而言叫做“页”,于物理内存而言叫“帧”,原因如下:

  • 提高内存空间利用,减少外部碎片,内部碎片相对可控
  • 提高内外存交换的效率,更细的粒度带来了更高的灵活度
  • 与虚拟内存机制呼应,便于使用页表建立虚拟地址到物理地址之间的映射
  • linux 页/帧的大小固定为4KB,实践得到的经验值,太大的话会增加碎片率,太小的话增加分配频率影响效率

1.4 Golang内存模型

几个核心的设计要点:

  1. 空间换时间,一次缓存,多次复用:因为每次向操作系统申请内存的操作很重,那么不妨一次多申请一些,以备后用。Golang的堆mheap正是基于该思想产生的数据结构,对于操作系统而言,这是用户进程中缓存的内存;对于Go进程内部,堆是所有对象的内存起源。
  2. 多级缓存,实现无/细锁化:堆是Go运行时最大的临界共享资源,这意味着每次存取都要加锁,很影响性能。为了解决这个问题,Golang依次细化粒度,建立了mcentral,mcache的模型:
    1. mheap:全局的内存起源,访问要加全局锁。数量只有1个
    2. mcentral:每种对象大小规格对应的缓存,锁的粒度仅仅局限于同一种规格里,全局划分为68份。(BigCache也用到了类似的思想)数量有68*2个(详细见2.3)
    3. mcache:每个P(即goroutine 内核线程)持有的一份内存缓存,访问时没有锁。数量等于P的数量
  3. 多级规格,提高利用率
    1. page:最小的存储单元,借鉴操作系统分页管理的思想,每个最小的存储单元也称之为页,但是大小为8KB
    2. mspan:最小的管理单元,大小是page的整数倍,从8B到80KB被划分为67种不同的规格,分配对象时,会根据大小映射到不同规则的mspan,从中获取空间。产生的特点
      1. 根据规格大小,产生了等级的制度,才使得central支持细锁化
      2. 消除了外部碎片,但是不可避免地会有内部碎片
      3. 宏观上能提高整体空间利用率

2.核心概念

2.1 内存单元mspan

mspan是Golang内存管理地最小单元,大小是page地整数倍,而且内部的页是连续的。

每个mspan会根据空间大小以及面向分配对象的大小,被划分为不同的等级,同等级的mspan会从属一个mcentral,最终被组织成链表,带有前后指针(prev/next),而且因为同属一个mcentral,所以是基于同一把互斥锁管理。

基于bitMap辅助快速找到空闲内存块,块大小为对应等级下的object大小,这里用到了Ctz64算法

type mspan struct {
    // 标识前后节点的指针 
    next *mspan     
    prev *mspan    
    // ...
    // 起始地址
    startAddr uintptr 
    // 包含几页,页是连续的
    npages    uintptr 


    // 标识此前的位置都已被占用 
    freeindex uintptr
    // 最多可以存放多少个 object
    nelems uintptr // number of object in the span.


    // bitmap 每个 bit 对应一个 object 块,标识该块是否已被占用
    allocCache uint64
    // ...
    // 标识 mspan 等级,包含 class 和 noscan 两部分信息
    spanclass             spanClass    
    // ...
}

Ctz64算法是一种用于计算一个数的二进制表示中尾随零(trailing zeros)的数量的算法

2.2 内存单元等级 spanClass

mspan根据空间大小和面向分配对象的大小,被划分为67种等级,分别是1~67,实际上隐藏的0级用于处理更大的对象

mspan等级列表,<font style="color:rgb(25, 27, 31);">runtime/sizeclasses.go</font>文件里

  • class:mspan等级标识,1~67
  • bytes/obj:该大小规格的对象会从这个mspan里获取空间,创建对象过程中,大小会上取整为8B的整数倍,因此该表可以直接实现object到mspan等级的映射
  • bytes/span:该等级的mspan的总空间大小
  • object:该等级的mspan最多可以new多少个对象,也就是span/obj
  • tail waste:span%obj的值
  • max waste:当一个对象被分配在某个size class中时,由于对象大小与span大小不完全匹配,可能导致的内存空间未被充分利用的情况

以class 3的 mspan为例,class分配的object大小统一为24B,只有17~24B大小的object会被分配到class3,最坏情况为,object 大小为17B,浪费空间比如为 ((24-17)*341 + 8 ) / 8192 = 28.24%

每个object还有一个重要的属性是nocan,标识了object是否含有指针,在gc时是否需要展开标记。

golang里,把span class 和 nocan两个信息组装为一个uint8,形成完整的spanClass标识。高7位标识span等级,最后一位标识nocan信息。

// A spanClass represents the size class and noscan-ness of a span.
//
// Each size class has a noscan spanClass and a scan spanClass. The
// noscan spanClass contains only noscan objects, which do not contain
// pointers and thus do not need to be scanned by the garbage
// collector.
type spanClass uint8

const (
	numSpanClasses = _NumSizeClasses << 1
	tinySpanClass  = spanClass(tinySizeClass<<1 | 1)
)

func makeSpanClass(sizeclass uint8, noscan bool) spanClass {
	return spanClass(sizeclass<<1) | spanClass(bool2int(noscan))
}

func (sc spanClass) sizeclass() int8 {
	return int8(sc >> 1)
}

func (sc spanClass) noscan() bool {
	return sc&1 != 0
}

2.3 线程缓存mcache

mcache是每个goroutine独有的缓存,因此没有锁。

mcache把每种span class等级的mspan各缓存了一个,并且因为nocan的值也有2种(有指针 无指针),所以总数是2*68=136个

mcache还有一个对象分配器 tiny allocator,用于处理小于16B对象的内存分配。

type mcache struct {
	_ sys.NotInHeap

	// The following members are accessed on every malloc,
	// so they are grouped here for better caching.
	nextSample uintptr // trigger heap sample after allocating this many bytes
	scanAlloc  uintptr // bytes of scannable heap allocated

	// Allocator cache for tiny objects w/o pointers.
	// See "Tiny allocator" comment in malloc.go.

	// tiny points to the beginning of the current tiny block, or
	// nil if there is no current tiny block.
	//
	// tiny is a heap pointer. Since mcache is in non-GC'd memory,
	// we handle it by clearing it in releaseAll during mark
	// termination.
	//
	// tinyAllocs is the number of tiny allocations performed
	// by the P that owns this mcache.
	tiny       uintptr
	tinyoffset uintptr
	tinyAllocs uintptr

	// The rest is not accessed on every malloc.

	alloc [numSpanClasses]*mspan // spans to allocate from, indexed by spanClass

	stackcache [_NumStackOrders]stackfreelist

	// flushGen indicates the sweepgen during which this mcache
	// was last flushed. If flushGen != mheap_.sweepgen, the spans
	// in this mcache are stale and need to the flushed so they
	// can be swept. This is done in acquirep.
	flushGen atomic.Uint32
}

2.4 中心缓存mcentral

每个mcentral对应一种spanclass,而且聚合了该spanclass下的mspan。

mcentral下的msapn分为2个链表,有空间msapn链表partial和满空间mspan链表full

每个mcentral一把锁

注意partial和full链表都包含两组mspan,一组是已清扫的正在使用的spans,另一组是未清扫的正在使用的spans,在每个垃圾回收GC周期里,这两个角色会互换。

// Central list of free objects of a given size.
type mcentral struct {
	_         sys.NotInHeap
	spanclass spanClass

	// partial and full contain two mspan sets: one of swept in-use
	// spans, and one of unswept in-use spans. These two trade
	// roles on each GC cycle. The unswept set is drained either by
	// allocation or by the background sweeper in every GC cycle,
	// so only two roles are necessary.
	//
	// sweepgen is increased by 2 on each GC cycle, so the swept
	// spans are in partial[sweepgen/2%2] and the unswept spans are in
	// partial[1-sweepgen/2%2]. Sweeping pops spans from the
	// unswept set and pushes spans that are still in-use on the
	// swept set. Likewise, allocating an in-use span pushes it
	// on the swept set.
	//
	// Some parts of the sweeper can sweep arbitrary spans, and hence
	// can't remove them from the unswept set, but will add the span
	// to the appropriate swept list. As a result, the parts of the
	// sweeper and mcentral that do consume from the unswept list may
	// encounter swept spans, and these should be ignored.
	partial [2]spanSet // list of spans with a free object
	full    [2]spanSet // list of spans with no free objects
}

2.5 全局堆缓存mheap

堆是操作系统虚拟内存的抽象,以页8KB为单位,其中页是作为最小内存存储单元的。

mheap可以把连续页组装成mspan。

全局内存基于bitMap标识使用情况,每个bit对应一页,为1标识已经被mspan组装。

通过heapArena聚合页,记录了页到mspan的映射信息(详见2.7)

建立空间页基数树索引radix tree index,辅助快速寻找空闲页(详见2.6)

是mcentral的持有者,持有所有spanClass下的mcentral,作为自身的缓存。

内存不够时,向操作系统申请,申请单位是heapArena(64M)

type mheap struct {
    // 堆的全局锁
    lock mutex
    // 空闲页分配器,底层是多棵基数树组成的索引,每棵树对应 16 GB 内存空间
    pages pageAlloc 
    // 记录了所有的 mspan. 需要知道,所有 mspan 都是经由 mheap,使用连续空闲页组装生成的
    allspans []*mspan
    // heapAreana 数组,64 位系统下,二维数组容量为 [1][2^22]
    // 每个 heapArena 大小 64M,因此理论上,Golang 堆上限为 2^22*64M = 256T
    arenas [1 << arenaL1Bits]*[1 << arenaL2Bits]*heapArena
    // 多个 mcentral,总个数为 spanClass 的个数
    central [numSpanClasses]struct {
        mcentral mcentral
        // 用于内存地址对齐
        pad      [cpu.CacheLinePadSize - unsafe.Sizeof(mcentral{})%cpu.CacheLinePadSize]byte
    }
    // ...
}

Golang内存模型和分配机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2253450.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

重生之我在异世界学编程之C语言:操作符篇

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言正文1. 算术操作符2. 关系&#xff0…

深度学习图像增强介绍

目录 一、引言二、常用数据增广方法三、图像变换类3.1 AutoAugment3.2 RandAugment 四、图像裁剪类4.1 Cutout4.2 RandomErasing4.3 HideAndSeek 五、图像混叠5.1 Mixup5.2 Cutmix 六、结论 一、引言 在图像分类任务中&#xff0c;图像数据的增广是一种常用的正则化方法&#…

HBU深度学习实验14-循环神经网络(1)

前言&#xff0c;预备知识 循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一类具有短期记忆能力的神经网络&#xff0e;在循环神经网络中&#xff0c;神经元不但可以接受其他神经元的信息&#xff0c;也可以接受自身的信息&#xff0c;形成具…

使用GDI对象绘制UI时需要注意的若干细节问题总结

目录 1、一个bitmap不能同时被选进两个dc中 2、CreateCompatibleDC和CreateCompatibleBitmap要使用同一个dc作为参数 3、不能删除已经被选入DC中的GDI对象 4、使用完的GDI对象&#xff0c;要将之释放掉&#xff0c;否则会导致GDI对象泄漏 5、CreateCompatibleBitmap返回错…

【Java-数据结构篇】Java 中栈和队列:构建程序逻辑的关键数据结构基石

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 一、引言 1. 栈与队列在编程中的角色定位 栈和队列作为两种基本的数据结构&#xff0c;在众多编程场景中都有着独特的地位。它们为数据的有序…

洛谷P2670扫雷游戏(Java)

三.P2670 [NOIP2015 普及组] 扫雷游戏 题目背景 NOIP2015 普及组 T2 题目描述 扫雷游戏是一款十分经典的单机小游戏。在 n 行 m列的雷区中有一些格子含有地雷&#xff08;称之为地雷格&#xff09;&#xff0c;其他格子不含地雷&#xff08;称之为非地雷格&#xff09;。玩…

vue2:Cascader 级联选择器中加载两种不同的数据结构

前言 因UI调整,需要将el-tree控件更换为级联选择器,而在原树形控件中,加载了两种不同的数据结构,(参见vue2:树形控件el-tree中加载两种不同结构的数据_vue2 树形插件-CSDN博客)所以现在级联选择器中也需要加载这两种不同的数据结构。 问题 原本以为处理方式差不多,在…

【10】MySQL中的加密功能:如何使用MD5加密算法进行数据加密

文章目录 1. MySQL加密功能概述2. MD5加密算法3. 在MySQL中使用MD5加密4. 使用更安全的加密方法总结 在现代的数据库应用中&#xff0c;数据的安全性和隐私性变得尤为重要。无论是存储用户的个人信息&#xff0c;还是保护敏感的业务数据&#xff0c;确保这些数据不会被未授权访…

【SARL】单智能体强化学习(Single-Agent Reinforcement Learning)《纲要》

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…

前端上传后端接收参数为null

记录一下工作中的问题 前端明明把文件传到后台了&#xff0c;但是后台接收参数为null 原因&#xff1a; 前端上传文件的name和后端接收参数名称不匹配 前端 后端 把前端上传的name由upfile改为file即可 本来是很基本的小问题&#xff0c;但因为自己钻了牛角尖一直没搞定&…

Clickhouse MergeTree存储引擎

文章目录 MergeTree特点MergeTree核心参数- ORDER BY- PARTITION BY- PRIMARY KEY- SAMPLE BY- TTL- SETTINGS- index_granularity- index_granularity_bytes- min_index_granularity_bytes- enable_mixed_granularity_parts- use_minimalistic_part_header_in_zookeeper- min_…

【机器学习】—Transformers的扩展应用:从NLP到多领域突破

好久不见&#xff01;喜欢就关注吧~ 云边有个稻草人-CSDN博客 目录 引言 一、Transformer架构解析 &#xff08;一&#xff09;、核心组件 &#xff08;二&#xff09;、架构图 二、领域扩展&#xff1a;从NLP到更多场景 1. 自然语言处理&#xff08;NLP&#xff09; 2…

【算法】【优选算法】位运算(下)

目录 一、&#xff1a;⾯试题 01.01.判定字符是否唯⼀1.1 位图1.2 hash思路1.3 暴力枚举 二、268.丢失的数字2.1 位运算&#xff0c;异或2.2 数学求和 三、371.两整数之和四、137.只出现⼀次的数字 II五、⾯试题 17.19.消失的两个数字 一、&#xff1a;⾯试题 01.01.判定字符是…

深度学习中注意力机制介绍及seq2seq案例

一. 注意力机制介绍 普通机器翻译 图中表示的是一个中文到英文的翻译&#xff1a;欢迎 来 北京 → welcome to BeiJing。编码器首先处理中文输入"欢迎 来 北京"&#xff0c;通过GRU模型获得每个时间步的输出张量&#xff0c;最后将它们拼接(按位相加)成一个中间语义张…

工业—使用Flink处理Kafka中的数据_ChangeRecord2

使用 Flink 消费 Kafka 中 ChangeRecord 主题的数据&#xff0c;每隔 1 分钟输出最近 3 分钟的预警次数最多的 设备&#xff0c;将结果存入Redis 中&#xff0c; key 值为 “warning_last3min_everymin_out” &#xff0c; value 值为 “ 窗口结束时间&#xff0c;设备id” &am…

Android 消息队列之MQTT的使用:物联网通讯,HTTP太重了,使用MQTT;断网重连、注册、订阅、发送数据和接受数据,实现双向通讯。

目录&#xff1a; 问题MQTT是什么以及为什么使用如何使用&#xff1a;第一阶段、基础功能如何使用&#xff1a;第二阶段、增加断网重连如何使用&#xff1a;第三阶段、封装 一、问题 在开发的时候&#xff0c;我们一般都使用Http和后台进行通讯&#xff0c;比如我们是开发物联…

项目-02-数学学院后台项目开发过程中的问题总结

目录 一、后台&#xff08;pc端&#xff0c;vue2&#xff09;1. dialog对话框被黑色蒙层盖住2. 将前端表格导出为word文档3. 在线查看、下载 .docx、.doc、.pdf文档 一、后台&#xff08;pc端&#xff0c;vue2&#xff09; 1. dialog对话框被黑色蒙层盖住 问题&#xff1a; d…

大数据实验E5HBase:安装配置,shell 命令和Java API使用

实验目的 熟悉HBase操作常用的shell 命令和Java API使用&#xff1b; 实验要求 掌握HBase的基本操作命令和函数接口的使用&#xff1b; 实验平台 操作系统&#xff1a;Linux&#xff08;建议Ubuntu16.04或者CentOS 7 以上&#xff09;&#xff1b;Hadoop版本&#xff1a;3…

使用Tomcat搭建简易文件服务器

创建服务器 1. 复制一个tomcat服务器&#xff0c;并命名为file-service(好区分即可) 2.在webapp里面新建一个文件夹 uploadfiles ,用于存储上传的文件 3. 修改conf/service.xml,配置文件服务器的端口与上传文件夹的访问 在Host标签之间加入一个Context标签 docBase"uploa…

【算法】位运算合集

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 零&#xff1a;位运算基础公式 零&#xff1a;五道基础题 1&#xff1a;位1的个数 2&#xff1a;比…