【Go专家编程——内存管理——内存分配】

news2024/9/19 22:18:09

1.内存分配

1.1 基础概念

编写过C语言的读者一定指导malloc()函数用于动态申请内存,其中内存分配器使用glic提供的ptmalloc2。

内存分配器

  • c语言的ptmalloc2
  • google的tcmalloc
  • facebook的jemalloc
  • 后两者在避免内存碎片和性能上均比glibc有较大优势,在多线程下更为明显。

Go语言也实现了内存分配器,原理与tcmalloc类似,简单地来说就是维护一块大的全局内存,每个线程(Go中为处理器P)维护一块小的私有内存,私有内存不足时再从全局申请。

为了方便管理,一般上的做法是向系统申请一块内存,然后将内存切割成小块,通过一定高度内存分配算法管理内存。以64位操作系统为例,Go程序启动时向系统申请的内容如下。
在这里插入图片描述
预申请的内存划分为spans、bitmap、arena三部分。

  • arena即所谓的堆区
  • spans和bitmap是为了管理arena区而存在的
  • arena区域划分为一个个的page§,每个页的大小为8KB,一共有64M(512GB/8KB)个页
  • spans区域存放span的指针,每个指针对应一个或多个page。所以spans的大小为64M*8byte = 512MB
  • bitmap区域的大小也是通过arena计算出来的,不过主要用于GC

1.1.1 span

span是用于管理arena页的关键数据,每个span中包含1个或多个连续页。

  • 为了满足小对象分配,则会将span中的一页划分为更小的粒度
  • 每一页中只存相同类型的对象
  • 对于大对象比如超过页大小,则通过多页实现

span的数据结构

type mspan struct{
	next *mspan		//链表后向指针
	prev *mspan		//链表前向指针
	startAddr uintptr		//起始地址,即所管理页的地址
	npages uintptr		//管理的页数
	nelems uintptr		//块个数,即有多少个块可供分配
	allocBits *gcBits		//分配位图,每一位代表一个块是否已分配
	allocCount uint16		//已分配块的个数
	spanclass spanClass		//class表中的class ID
	elemsize	uintptr		//class表中的对象大小,即块大小
}

1.1.2 cache

有了管理内存的基本单位span,还需要有个结构来管理span。这个数据结构就叫做mcentral,各个线程需要内存时从mcentral管理的span中申请内存,为了避免多线程申请内存时不断地加锁,Go为每个线程分配了span的缓存,这个缓存即cache。

type mcache struct {
	alloc [67*2] *mspan	// 按class分组的mspan列表
}

alloc为mspan的指针数组,数组大小为class总数的2倍。

  • 数组中的每个元素代表一种class类型的span列表
  • 每一种class类型都有两组span列表
    • 第一组列表中所表示的对象包含了指针
    • 第二组列表中所表示的对象不包含指针
    • 目的:对于不包含指针的span列表,不需要去做GC扫描
  • cache在初始化时是没有任何span的,在使用过程中会动态地从central中获取并缓存下来。

1.1.3 central

cache作为线程的私有资源为单个线程服务,而central则是全局资源,为多个线程服务,当某个线程的内存不足时会向central申请,当某个线程释放内存时又会回收进central。

type mcentral struct{
	lock	mutex		// 互斥锁
	spanclass spanClass		//span class ID
	nonempty mSpanList		//指还有空闲块的span列表
	empty	mSpanList		//指没有空闲的span列表
	nmalloc uint64		//已累计分配的对象个数
}
  • lock:线程间的互斥锁,防止多线程读写冲突
  • spanclass:每个mcentral管理一组有相同class的span列表
  • nonempty:指还有内存可用的span列表
  • empty:指没有内存可用的span列表
  • nmalloc:指累计分配的对象个数

线程从central中获取span的步骤如下

  1. 加锁
  2. 从nonempty列表获取一个可用span,并将其从链表中删除
  3. 将取出的span放入empty链表
  4. 将span返回给线程
  5. 解锁
  6. 线程将该span缓存进cache

线程将span归还的步骤如下

  1. 加锁
  2. 将span从empty列表中删除
  3. 将span加入nonempty列表
  4. 解锁

1.1.4 heap

由central的数据结构可见,每个mcentral对象只管理特定的class规格的span。事实上每种class都会对应一个mcentral,这个mcentral的集合存放于mheap数据结构中:

type mheap struct{
	lock mutex
	spans []*mspan
	bitmap uintptr		//指向bitmap的首地址,bitmap是从高地址向低地址递增的
	arena_start uintptr		//指示arena区域的首地址
	arena_used	uintptr		//指示arena区域已使用的地址位置
	central[67*2]struct{
		mcentral mcentral
		pad [sys.CacheLineSize - unsafe.Sizeof(mcentral{})%sys.CacheLineSize] byte
	}
}
  • lock:互斥锁
  • spans:指向spans区域,用于映射span和page的关系
  • bitmap:bitmap的起始地址
  • arena_start:arena区域的首地址
  • arena_used:当前arena已使用区域的最大地址
  • central:每种class对应的两个mcentral

由数据结构可见,mheap管理着全部的内存,事实上Go就上通过一个mheap类型的全局变量进行内存管理的。

2.内存分配的流程

针对待分配对象大小的不同有不同的分配逻辑

  • (0,16B)且不包含指针的对象,Tiny分配
  • (0,16B)且不包含指针的对象,正常分配
  • [16B,32KB]:正常分配
  • (32KB,----):大对象分配

Tiny分配和大对象分配都属于内存管理的优化范畴,这里暂时仅关注一般的分配方法。

以申请size为n的内存为例:

  1. 获取当前线程的私有缓存mcache
  2. 根据size计算出适合的class的ID
  3. 从mcache的alloc[class]链表中查询可用的span
  4. 如果mcache中没有可用的span,从mcentral申请一个新的span加入mcache
  5. 如果mcahce中也没有可用的span,从mheap中申请一个新的span加入mcentral
  6. 从该span中获取空闲对象地址并返回

3.小结

Go的内存分配是一个相当复杂的过程,其中还掺杂了GC的处理。

  • Go程序启动时申请了一大块内存,划分为spans、bitmap、arena区域
  • arena区域按页划分成一个个小块
  • span管理一个或多个页
  • mcentral管理多个span供线程申请使用
  • mcache作为作为线程的私有资源,资源来源于mcentral

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

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

相关文章

VBA即用型代码手册:删除Excel中空白行Delete Blank Rows in Excel

我给VBA下的定义:VBA是个人小型自动化处理的有效工具。可以大大提高自己的劳动效率,而且可以提高数据的准确性。我这里专注VBA,将我多年的经验汇集在VBA系列九套教程中。 作为我的学员要利用我的积木编程思想,积木编程最重要的是积木如何搭建…

HTTP响应的基本概念

目录 HTTP响应中的一些信息 HTTPS HTTP响应中的一些信息 状态码:描述了这次HTTP请求是否成功,以及失败的原因。 1)200 ---OK 表示这次访问成功了。 2)404 ---Not Found 表示客户端请求的资源在服务器这边不存在。 3&a…

93.网络游戏逆向分析与漏洞攻防-游戏技能系统分析-增强技能信息显示后进行分析

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 如果看不懂、不知道现在做的什么,那就跟着做完看效果,代码看不懂是正常的,只要会抄就行,抄着抄着就能懂了 内容…

Pytorch深度学习实践笔记3

🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:pytorch深度学习 🎀CSDN主页 发狂的小花 🌄人生秘诀:学习的本质就是极致重复! 视频来自【b站刘二大人】 目录 1 梯度下降&#…

html简述——part1

HTML概述 HTML(HyperText Markup Language)是一种用于创建网页的标准标记语言,具体指超文本标记语言。它不是一种编程语言,而是一种标记语言,用于描述网页的结构和内容。通过HTML,开发者可以定义网页的标题…

【算法】递归、搜索与回溯——简介

简介:递归、搜索与回溯,本节博客主要是简单记录一下关于“递归、搜索与回溯”的相关简单概念,为后续算法做铺垫。 目录 1.递归1.1递归概念2.2递归意义2.3学习递归2.4写递归代码步骤 2.搜索3.回溯与剪枝 递归、搜索、回溯的关系: …

广告圈策划大师课:活动策划到品牌企划的深度解析

对于刚接触营销策划的新人来说,在这个知识密集型行业里生存,要学习非常多各种意思相近的概念,常常让人感到头疼,难以区分。 这里对这些策划概念进行深入解析,帮助您轻松理清各自的含义和区别。 1. 活动策划&#xff…

CCF20230901——坐标变换(其一)

CCF20230901——坐标变换&#xff08;其一&#xff09; #include<bits/stdc.h> using namespace std; int main() {int n,m,x[101],y[101],x1[101],y1[101];cin>>n>>m;for(int i0;i<n;i)cin>>x1[i]>>y1[i];for(int j0;j<m;j)cin>>x[…

PD协议:引领电子设备充电新时代

随着科技的飞速发展&#xff0c;电子设备已成为我们日常生活中不可或缺的一部分。然而&#xff0c;这些设备的充电问题一直困扰着广大用户。传统的充电方式不仅效率低下&#xff0c;而且存在着安全隐患。为了解决这一问题&#xff0c;USB Implementers Forum&#xff08;USB-IF…

IPv6 地址创建 EUI-64 格式接口 ID 的过程

IPv6 接口标识符 IPv6 地址中的接口标识符&#xff08;ID&#xff09;用于识别链路上的唯一接口&#xff0c;有时被称为 IPv6 地址的 “主机部分”。接口 ID 在链路上必须是唯一的&#xff0c;始终为 64 位长&#xff0c;并且可以根据数据链路层地址动态创建。 MAC 地址 中的…

【C++项目】实时聊天的在线匹配五子棋对战游戏

目录 项目介绍 开发环境 核心技术 项目前置知识点介绍 Websocketpp 1. WebSocket基本认识 2. WebSocket协议切换原理解析 3. WebSocket报文格式 4. Websocketpp介绍 5. 搭建一个简单WebSocket服务器 JsonCpp 1. Json格式的基本认识 2. JsonCpp介绍 3. 序列化与反序…

在ubuntu中关于驱动得问题:如何将nouveau驱动程序加入黑名单和安装NVIDIA显卡驱动

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、nouveau驱动程序加入黑名单二、安装NVIDIA显卡驱动 一、nouveau驱动程序加入黑名单 (1) 打开黑名单列表文件 终端输入&#xff1a; sudo gedit /etc/modprobe…

CCF20230501——重复局面

CCF20230501——重复局面 代码如下&#xff1a; #include<bits/stdc.h> using namespace std; int main() {int n;cin>>n;char a[101][64];int i,j;for(i0;i<n;i){for(j0;j<64;j){cin>>a[i][j];}}int temp0,flag1;for(i0;i<n;i){flag1;for(j0;j<…

Linux程序开发(十二):线程与多线程同步互斥实现抢票系统

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

Mongodb分布式id

1、分布式id使用场景 分布式ID是指在分布式系统中用于唯一标识每个元素的数字或字符串。在分布式系统中&#xff0c;各个节点或服务可能独立运行在不同的服务器、数据中心或地理位置&#xff0c;因此需要一种机制来确保每个生成的ID都是全局唯一的&#xff0c;以避免ID冲突。 …

Pytorch线性模型(Linear Model)

基本步骤 ①首先准备好数据集&#xff08;DataSet&#xff09; ②模型的选择或者设计&#xff08;Model&#xff09; ③进行训练&#xff08;Train&#xff09;大部分模型都需要训练&#xff0c;有些不需要。这一步后我们会确定不同特征的权重 ④推理&#xff08;inferring…

就业班 第三阶段(ELK) 2401--5.20 day1 ELK 企业实战 ES+head+kibana+logstash部署(最大集群)

ELKkafkafilebeat企业内部日志分析系统 1、组件介绍 1、Elasticsearch&#xff1a; 是一个基于Lucene的搜索服务器。提供搜集、分析、存储数据三大功能。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff…

学习单向链表带哨兵demo

一、定义 在计算机科学中&#xff0c;链表是数据元素的线性集合&#xff0c;其每个元素都指向下一个元素&#xff0c;元素存储上并不连续。 1.可以分三类为 单向链表&#xff0c;每个元素只知道其下一个元素是谁 双向链表&#xff0c;每个元素知道其上一个元素和下一个元素 …

mySql从入门到入土

基础篇 在cmd中使用MYSQL的相关指令&#xff1a; net start mysql // 启动mysql服务 net stop mysql // 停止mysql服务 mysql -uroot -p1234//登录MYSQL&#xff08;-u为用户名-p为密码&#xff09; //登录参数 mysql -u用户名 -p密码 -h要连接的mysql服务器的ip地址(默认1…

记一次安卓“Low on memory“崩溃问题

前言 最近再调人脸识别算法相关demo,发现调试期间总是偶发性崩溃&#xff0c;捕获不到异常的那种&#xff0c;看日志发现原因是Low on memory&#xff0c;一开始还疑惑 App内存不够应该是OOM啊,怎么会出现这种问题&#xff0c;百思不得其解&#xff0c;直到我打开了 Android s…