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
比较判断的时候把具体的值写在前面,防止出现将==写成=赋值的情况