C++内存管理 第三章 malloc/free
malloc分配出的内存块

组成:
debug header:关于debug调试的一些信息,后面有详细解释
block size:实际返回的用户需要申请的空间,用户所需要的指针也是指向这里
debug tail:同debug header
pad:内存对齐相关,分配的内存块必须是16字节的倍数
cookie:存放分配的内存块的大小
VC6和VC10的malloc比较
CRT:C Run Time Library
SBH:Small Block Heap
VC6:申请的内存小于1016字节,使用SBH服务(为什么是1016,因为1016+额外的cookie是8字节,正好是1024=1K),否则使用操作系统的HeapAlloc;
VC10:全部交由操作系统管理,即SBH部分的内容被整合到操作系统内部。
heap_init和ioinit在VC和VC10都存在


VC6的内存分配流程
函数调用栈(从下网上看),其中main()函数就是我们自己写的main()函数。
_heap_init()
_heap_init是分配了16个head,每个head管理1M内存,1M能够快速分配,快速回收。head数据结构如下:

绿色部分表示16个header,每个header包括两个指针,一个指针指向管理的内存,一个指向管理内存的中心。其余的bit用来存放链表的状态,详情在下面。
BITVEC:unsigned int
bitvCommit:32个bit
bitEntryHi、bitEntryLo:合计64个bit
_ioinit()
第一次分配内存,如果不是debug模式直接调用malloc,如果是debug模式则调用_malloc_dbg。
这里采用debug模式。

_heap_alloc_dbg()
上图的_malloc_crt(IOINFO_ARRAY_ELTSsizeof(ioinit))分配的是328(ioinfo大小为6,对齐为8)=256字节(100H),就是下面的nSize,加上前面_CrtMemBlockHeader(这块是附加上去给debuger时用的东西),再加上尾部的gap。得到的blockSize需要判断(是否大于1016?见下面的_heap_alloc_base())

1.pBlockHeaderNext, 指向链表后内存块
2.pBlockHeaderPre, 指向链表前内存块
3.szFileName, 记录申请使用该内存空间所在的文件
4.nLine, 记录申请使用该内存空间所在文件的行号
5.nDataSize, 实际数据区的空间大小(应用程序实际使用到的内存块)
6.nBlockUse, 表示当前内存块的类型, 如 _CRT_BLOCK、_NORMAL_BLOCK 等…
7.IRequest, 操作流水号
8.gap[nNoMansLandSize], 上下两处“栏杆”,保护应用程序实际使用到的内存,到发生内存越界的情况,调试器可以检查到

如上图所示,调用_heap_alloc_base()获取每个blockSize内存的指针,在debug模式下malloc 分配的内存都被管理在链表中(及时已被客户使用),通过头尾指针将所有分配出来的block串接起来。
_heap_alloc_base()
根据申请的大小来判断是否大于1016,因为留给cookie的8字节内存,上下cookie各4字节。若大于1016则交由操作系统分配;若小于等于则由_sbh_alloc_block()分配。

_sbh_alloc_block()
用于字节对齐,加首位cookie,如下图0x100+0x24+4*2=0x12C=>0x130,以16字节对齐,其中cookie表示该块的大小,再将最后一个bit置为1表示这个块被分配出去。

进行到这里,都还是在计算所需分配的内存的大小,仍没有真正地开始实际的分配内存的操作
_sbh_alloc_new_region()
开始进行实际分配内存的前置操作(构建一个region控制台,仍未开始分配内存),region内部如图:

_sbh_alloc_new_group()

总体就是花费16K的内存来管理1M的内存空间。其中1M被分为32组,对应32个Group。每个组由32对指针构成的双向链表(类似分配器的8字节隔开的),每组的32K又被分为8个page,一个page是4K,使用链表将这些page连起来,并连接到对应的双向链表中,完成这些之后申请一块内存,得到内存,初始化,内存申请就成功了。
层层return

第一次分配的内存大小为130H,将cookie装好并返回其指针007d0ed0,这里的指针并不指向用户需要的内存,最后将_sbh_alloc_new_region分配好的内存地址层层返回得到申请的内存地址。
多次分配

在一个page上继续切分,因为其初始的其他链表上都没内存块,如果其他链表上挂有内存块会从最近的找所需的块。如果一个组都不够会继续申请第二个组。
内存释放
将一个内存块的cookie中的状态置为未分配,将内存变为两个指针,指向负责管理内存的链表,header中的bit也发生变化,表示未分配。
回收相邻的内存。应该有个合并策略(这就是为什么要有下cookie的原因),要不然会出现内存碎片的问题。

内存分配总结
上述的分配策略,总的思想是一个分段管理。至于为什么有16个头,32个组,1个头管理1M内存,这些都是经验值,有利于操作系统。
全回收的动作会被延缓,并不会只要归还所有内存之后就把这么多段的内存整合还给操作系统(defer)。当第二个全回收出现的时候才会把内存归还操作系统。
本文详细解析了C++中malloc和free的内存分配过程,重点对比了VC6和VC10的内存管理机制,介绍了heap_init和heap_alloc_dbg函数的作用,以及SmallBlockHeap在不同版本中的变化。内容涉及debugheader、内存对齐、内存块类型和释放策略。
&spm=1001.2101.3001.5002&articleId=138446227&d=1&t=3&u=70b15014401c4ac38d339603463caee3)

被折叠的 条评论
为什么被折叠?



