IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++内存管理(3):STL的alloc是如何运行的 -> 正文阅读

[C++知识库]C++内存管理(3):STL的alloc是如何运行的

在了解了分配器的进化过程之后,我们就进入了这个进化过程的终极一步:全局分配器。

全局分配器就是想通过“大块切小块”的管理方式,来减少malloc的次数从而减少cookie

全局分配器为了具有更好地适配性,通过管理16条链表来管理不同大小地内存分配。

image-20220529134818012

这就是我们接下来讨论的重点。

不同编译器下的allocator实现

首先,我们来看看常见的几个编译器中,内存分配的实现。

VC6

VC6中的malloc在申请空间时的布局如下:

image-20220529135333862

可以看到增加了cookie,debug header等内容,这也就是前面所说的在堆上分配内存时的情况。

VC6中也有标准分配器std::allocator,但看到源码后就会发现,其本质是调用了全局的operator_new()和operator_delete(),也就是调用了malloc和free,没有特殊设计。

image-20220529135826478

BC5

BC5这个版本也依旧是使用了全局的operator_new()和operator_delete(),没有特殊的设计。

image-20220529135940887

GNU2.9

在GNU2.9的标准库中,std::allocator的实现也与上述一致,没有特殊设计。

但设计者有明确指出,虽然按照惯例实现了std::allocator,但真正使用的并不是它,而是另一个std::alloc

image-20220529140448489

这里G2.9实现的alloc就是我们要说的特殊设计。

注:GNU2.9的alloc在GNU4.9中变为了_pool_alloc,功能一致。

alloc是如何运行的

接下来我们就来剖析alloc的特殊设计,也就是16条链表的结构。

alloc管理着16条链表,每个链表都各司其职,负责8字节到128字节的空间管理。

在这里插入图片描述

我们根据场景的变化,来看看alloc到底做了什么。

战备池与嵌入式指针

首先我们需要知道,每次在调用malloc时,其实就相当于挖一块大内存先分配给空闲空间(战备池,也叫内存池),再进行具体的分配操作。

嵌入式指针这部分和我们说的联合体结构一样,把一块空间的前四个字节当作指针。当给客户端时,就是一块完整的数据块,而当归alloc管理时,就会使用指针将其串连,一举两得。

在这里插入图片描述

空间的申请与指针管理

开始时链表处于空的状态。

image-20220529185322652

此时申请32bytes的空间,由于现在没有空闲空间,所以直接挖一块32bytes*20*2的空间作为空闲内存池。

注意这里是2倍,因为一半要给链表管理,一半放入内存池

将这块空间调整到16的倍数,为1280

此时把第一块给客户端,剩下的19*32bytes交给3号链表管理(32/16=2 2+1=3)。那么剩下的640就可以放入内存池了。

注意3号链表管理的这块空间在开头是有cookie的(蓝色部分)

image-20220529185940077

继续申请64bytes的空间,本应该是7号链表管理的,而现在上面没有内存,再去看内存空闲池发现有地方。

内存池的640可以分成10个64bytes,把第一个给客户端,后面9个由7号链表管理(注意图中的链表位置)

image-20220529190601086

继续申请96bytes的空间,这时11号链表上没有管理空间,而且内存池没有空闲,所以申请一大块空间。

这块空间是96bytes*20*2这么大,调整到16的倍数就是3920

把前面第一块给客户端,后面19块交给11号链表管理。

最后将剩下的2000bytes交给内存池。

注意这里的空间也是后来申请的,所以开头有cookie(红色)

image-20220529191959004

再申请88bytes的空间,此时内存池有2000的存量,可以分出88*20的空间,剩余240。88由第10号链表管理。

image-20220531193150190

再申请8bytes的空间,内存池空闲240,可以分出20*8的空间,剩余80,8由0号链表管理。

image-20220531193804621

这时再申请104bytes的空间,查找12号链表发现无内容,内存池也不够分配,于是准备继续申请内存。

注意在申请前先将80的余量分配给了9号链表。

申请104*20*2的大小,第一块给客户端,其他的19块交给11号链表管理,剩余2408

image-20220531194139412

申请112的空间,查找13号链表无内容,从内存池分配112*20,剩余168

由链表13管理。

image-20220531194437963

申请48bytes,内存池有168余量,可以分出来3块。其中一块给客户端,剩余两块由5号链表管理。附带24的空闲空间。

image-20220531194648228

再申请72bytes的空间,查找8号无可用,剩余24不足72,于是先把24交给2号链表管理。

然后想要申请。目前已申请了9688的内存,我们假设系统堆的大小为10000,剩下的就无法满足之前的索取(72*20*2)

我们发现与之最近的链表是9号,可以满足要求。

所以把9号的80给8号,拆出72还给客户端,剩余8。

image-20220531195454109

这时再申请72bytes,余量不够,先将剩余的8交给0号链表。

想要申请但是依旧失败,于是找到最近且可以满足的10号链表(88)

分出72给客户端,剩余16空闲。(注意这里的分出和剩余管理只是链表在变动,不会发生结构变化)

image-20220531200828076

再申请120bytes,先将剩余的16给链表1号管理。但无法索取,因为此时15号链表无内容可用,走到了末路。

一些值得讨论的点

当要求的内存大于128bytes使就会使用一级分配器。小于128bytes才会进入free-lists管理的二级分配器。

这里链表和链表指针的定义为

obj** my_free_list,*p

这是容易让人迷惑的,其实第二个是obj* p

而且我们发现,在整个过程中,只有malloc,而没有对应的free。

在deallocate中也没有调用free,只是把所有区块收集了起来,虽然算不上内存泄漏,但也不是一种非常好的设计。

当然,这种设计对比malloc来说,时间消耗要少很多。

但malloc究竟是不是真的慢,又或者说设计是否真的不够精妙,我们下回分解。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-25 17:54:42  更:2022-06-25 17:56:21 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 4:35:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码