C++内存管理(候捷)第二讲 笔记

VC6 malloc

在这里插入图片描述
cookie一定占用8个字节。上下各4个字节
Cookie 是一小段额外的标识信息,用于在边界检查中检测缓冲区溢出。它通常是一个特殊的值,放置在分配的内存块的开始位置。记录内存分配出去的大小
Debug Header 包含一些调试信息,例如分配的文件名、行号等。这些信息用于调试和跟踪内存分配的源
block size 分配的实际数据块
Debug Tail 包含与调试信息相关的尾部数据
pad 对齐填充

VC6标准分配器之实现

在这里插入图片描述
VC6标准分配器里面的allocate和deallocate的实现只是调用::operator new 和::operator delete, 没有任何特殊设计。底层是malloc和free

BC5标准分配器之实现

在这里插入图片描述
Borland5 编译器只是调用::operator new 和::operator delete, 没有任何特殊设计

G2 9标准分配器之实现

在这里插入图片描述
allocate和deallocate的实现也是调用::operator new 和::operator delete, 没有任何特殊设计
在这里插入图片描述
GNU C++2.9容器使用的分配器,不是std::allocator,而是std::alloc

G2.9 std::alloc VS G4.9 __pull_alloc

G2.9 std::alloc在G4.9中是 __pull_alloc

G4.9 __pull_alloc用例

G4.9版本中标准分配器std::allocator的实现,里面的allocate和deallocate的实现只是调用::operator new 和::operator delete, 没有任何特殊设计
在这里插入图片描述
__pull_alloc去除了cookie(一个元素带有上下两个cookie,共8B)。上图每个double相距8字节,下面的double相距10字节

G2.9 std::alloc

在这里插入图片描述
前文的Per class allocator每个类重载了operatpr new和operator delete。每个类单独维护一个链表

为了通用的将operator new的分配操作提取到alloc类。为了适应不同类的大小,std::alloc为所有的类维护16个链表,每个链表负责不同大小的区块分配,从小到大分别负责8B, 16B, 24B, 32B,…, 按8的倍数增长,最大是16 x 8 = 128B。当大小不为8的倍数的时候,分配器会自动将其对齐到8的倍数。当大小超过128B时,就交给malloc来单独处理。

当一个vector里面存了一个“stone”类型的对象,它的大小是32B,那么就会启用下面的#3的链表,它负责32B区块的分配。每次分一大块(里面有20个小块,每个都是32B,但是实际分配的时候是20个小块的两倍大小,剩余的部分暂且不用), 指定一个小块给stone即可
在这里插入图片描述
嵌入式指针:借用A对象所占用的内存空间中的前4个字节,这4个字节用来 链住这些空闲的内存块;
一旦某一块被分配出去,那么这个块的 前4个字节 就不再需要,此时这4个字节可以被正常使用
使用的时候看成对象实例,空闲的时候会看成next指针

G2.9 std::alloc运行一瞥

在这里插入图片描述
分配器的客户是给容器用的,不是给用户用的。如果直接用分配器,要记住分配了多大,因为没有cookie。再回收时告知分配器。容器所有的元素大小都一样,第一个模版参数是类型,直接sizeof可以得到大小
在这里插入图片描述
刚开始的时候pool为空,要分配32 * 20 *2 + RoundUp(0 >> 4)= 1280B大小的空间为pool,然后从pool里面切割20个小块(共640B)挂在#3链表上,剩下的640B放在pool里面备用
在这里插入图片描述
创建了新的容器,元素大小为64B,此时链表为空。由于上页的pool里面还剩640B,现在将其切分为640/64 = 10个区块,第一个给容器,剩下的9个挂在list #7. 此时pool的大小为0,因为被切分挂到链表上了
在这里插入图片描述
在上面的基础,新建容器,元素大小为96B,先检查pool是否有余量,由于pool为空,此时调用malloc分配一大块作为pool,总共大小为96x 20 x 2 + RoundUp(1280 >> 4) = 3840 + 80 = 3920B,其中20个区块拿出来用,1个区块返回给容器,另外19个区块挂在#11链表上,剩下的就是pool的容量,即3920−(96×20)=3920−1920=2000 B
在这里插入图片描述
新建容器,元素大小是88B,pool中的余量为2000,将2000切分为20个区块(每个区块88B),第一个区块给容器,剩下的19个区块挂在#10链表上。pool剩下的余量:2000 - 88 x 20 = 240B
在这里插入图片描述
容器连续申请3次88B大小的空间,由于已经在#10 链表上挂了19个大小为88B的区块,直接从该链表上拿下来3个区块返回给容器即可。pool大小还是240
在这里插入图片描述
新的容器,元素大小为8B,pool容量为240,从pool里面切分出20个区块,共8B x 20 = 160B大小,第一个返回给容器,其余19个区块挂在#0链表上pool剩240 - 160 = 80B
在这里插入图片描述
新的容器,元素大小为104B。pool余量为80B,此时这个80B大小的空间就是碎片,需要将其挂在某一个链表上。挂在80 / 8 - 1 = 9号链表。碎片处理完之后,再处理现在的需求104B的分配。
现在再调用malloc分配一大块,大小为104 x 20 x 2 + RoundUp(5200 >> 4) = 4160 + RoundUp(325) = 4160 + 328 = 4488,pool容量为4488 - 104 x 20 = 2408B,把第一个区块分配给容器,切出的19个挂在#12 list上。pool的余量为2408B。累计申请量为5200 + 4488 = 9688B
在这里插入图片描述
请112B,pool容量为2408,从里面取出112 * 20 = 2240,第一个区块返回给容器,留下19个区块挂在13号链表上。pool余量为2408 - 2240 = 168B。
在这里插入图片描述
申请48B,5号链表,pool余量168B。分配168 / 48 = 3个大小为48的区块,pool余量为24B
在这里插入图片描述
实验过程中把系统heap大小设置为10000,前面已经累计分配了9688B,因此无法满足本次内存分配
alloc从手中资源取最接近72B的大小回填pool,这里最接近的是80B(9号链表有1个空的可用),然后从80B中切72B给容器,pool剩下80 - 72 = 8B。
9号链表指针指向0
在这里插入图片描述
再申请72B,list #8没有区块可用,pool余量为8,先将pool余额挂在list #0。然后想要调用malloc分配72 x 20 x 2 + RoundUP(9688 >> 4) = 3488B的空间,无法满足分配
于是alloc从手中资源取最接近72B的88B(list #10)回填pool,因为上面的80B(list #9)已经用完了,从88B中切出72B返回给容器,pool余额:88 - 72 = 16B。
在这里插入图片描述
新的容器申请120B,挂在120 / 8 - 1 = 14号链表。pool不足。将上面的16B挂在1号链表上。
alloc从手中资源中取最接近120B的区块回填pool,但是14号链表和15号链表都是空的,于是无法分配。
在这里插入图片描述

G2.9 std::alloc源码剖析

分为两级分配器,分别是第一级分配器和第二级分配器。
第一级分配器不重要,主要模拟new handler的作用,处理一下内存分配的情况。
GC4.9不再有一级
在这里插入图片描述
常量:ALIGN 对齐量, MAX_BYTES元素块最大可分配量,以及NFREELISTS链表个数/数组长度

size_t ROUND_UP(size_t bytes) {
    return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
}

ROUND_UP是将分配的大小变成8的倍数,计算分配追加量
union_obj嵌入式指针,原本是两个成员,其中一个用不到(方便理解),因此改为struct

size_t FREELIST_INDEX(size_t bytes) {
    return (((bytes) + __ALIGN-1)/__ALIGN - 1);
}

FREELIST_INDEX根据bytes大小,指定到是哪个链表

char*   start_free = 0; // 指向战备池pool的头
char*   end_free = 0;   // 指向战备池pool的尾
size_t  heap_size = 0;  // 累计分配大小

在这里插入图片描述
volatile和多线程相关
my_free_list指向链表指针数组的指针
当n大小大于最大分配量,另外处理,直接malloc
refill(ROUND_UP(n)),链表充值,从pool中去拿区块。上述一览的过程

链表很长,大量分配,大量回收后,无法判断哪些是连续的,一定要连续的内存块才可以free,所以deallocate没有free,这个不算内存泄露
在这里插入图片描述
在这里插入图片描述
refill默认一次抓20,只是初值
调用chunk_alloc函数来挖一大块内存
当分配的区块为1时,就直接交给客户(容器)
chunk指的是一大块,然后将其切割,建立free list。refill的参数n表示一小个区块的大小
切割for循环从1开始,因为第一块内存要直接给出去
指针转为字节跳转后再转回obj*
在这里插入图片描述
pool空间满足20个区块的需求
pool空间只能满足1到19个区块的需求
pool不能满足1块
在这里插入图片描述
往更大内存链表挖内存时,只用一块来判断,因为无法确定链表里的内存是否连续
在这里插入图片描述
定义一些初始数据

G2.9 std::alloc观念大整理

在这里插入图片描述
容器c使用alloc分配空间,看16条链表中哪一条可以提供区块,分配给它,不带cookie
new出来的对象Foo(2),带有cookie,通过调用底层malloc,push_back的时候不带cookie
在这里插入图片描述
比较判断的时候把具体的值写在前面,防止出现将==写成=赋值的情况

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值