在设计一个内存池时,首先要对内存池的存储数据部分的构建做一个大概的规划。
因为是动态申请内存,没有办法预计将来正在运行的程序究竟会需要多大的内存空间,因此在内存池的设计上要预留空间,未来防止盲目的使用过大空间,采用的方法就是用多个内存块组成一个内存池,第一次分配的时时,先申请一个内存块,当程序不够用的时候,再向系统申请一块内存,然后将新内存与前面已获得的内存块组织成链表,以后一旦发现内存不够,那么就如此继续申请后续内存块,从而组成一个多内存块的内存池。内存池中内存块的长度可以相等,也可以不相等。
因为 STL容器中的元素大小都是相等的,故在此将内存块的存储区域划分为一个一个大小为 nunitsize 的存储单位。
程序一旦由系统获得内存池所需内存之后,在程序把内存池归还系统之前,系统绝不会再对这些内存进行任何管理工作,要么由程序委派给其它部件,在 STL中,由于空间配置器的存在,那么内存池的管理工作就是由它来执行。
首先要管理的就是内存块的信息表
struct memoryblock
{
int nsize; // 该内存的大小
int nfree; //该内存还有多少可分配的单位
int nfirst; // 当前可分配的第一个单位序号
memoryblock *pnext; // 指向下一个内存块
char adata[1] //用于标记分配单元开始的位置
}
本例将内存块的信息表 Memoryblock 安排在前面,即在一个内存块中,前面的部分为内存块信息表,紧接着表的便是内存块的数据区域,未来能够获得数据存储去的首地址,在信息表 memoryblock 的最后位置安排了 域 adata[1],这里 adata 就是数据存储去的首地址。
对于内存块中空闲存储单位的管理就是内存池的主要管理工作。本例在对这些空间存储区域进行管理时,采用了一个小技巧,即把它们组织成链表,而链表中那些用于指向下一节点的 “指针” 就保存在各个空闲单位的前两个字节,从而免去了为这些“ 指针” 按照存储空间消耗。 至于空闲存储单位链表的头指针则保存于 表 memoryblock 的nfirst 域。需要注意的是,这里所说的空闲存储单位链表各个节点的指针实在内存块初始化时为这些节点安排的序号而不是真正的地址,当然这个序号可以看成是内存块中的内部地址。
链表头 memorypool 的信息如下
struct memorypool
{
int ninitsize;///首块长度
int ngrowsize;///后续块长度
int nunitsize;/// 定义存储单位大小
memoryblock *pblock; //指向内存块链表的指针
}