一、SGI标准的空间配置器,std::allocator
SGI定义了一个符合部分标准,名为allocator的配置器,但由于效率不佳,其内实现仅为将new和delete做了一层简单封装,故没有被建议使用。
二、SGI特殊的空间配置器,std::alloc
双层级配置器,第一级配置器直接使用malloc和free,第二级配置器则根据情况采用不同的策略,具体策略为:当配置区块超过128bytes,视为大区块,直接调用第一级配置器;当配置区块小于128bytes,视为小区块,采用内存池管理方式。在SGI STL中同时采用了第一级和第二级两种配置器的方式。
一个重要的流程图如上。根据是否定义__USE_MALLOC,来确定是否只采用第一级配置器,或是第一第二级配置器并存。其中simple_alloc作为了一个类接口,封装了空间配置器的内存申请和释放。
三、第一级配置器,__malloc_alloc_template
第一级配置器的实现主要是封装了malloc,free,realloc等系统函数。看一下具体实现。
其中这里的oom_xxx是函数指针,对应了不同情况下处理内存不足。在函数中循环调用“内存不足处理例程”,这里的“处理例程”自行设定,如果没有实现,oom_xxx将抛出bad_alloc异常信息,或者直接exit(1)终止程序。
四、第二级配置器,__default_alloc_template
第二级配置器采用了一些策略,用于避免太多太小额区块造成的内存碎片。具体策略为:如果区块超过128bytes,则交给第一级配置器处理,如果小于128bytes,则以内存池管理(每次配置一块内存,都维护对应的自由链表free-list,下次如果有相同大小的内存需求,直接从free-list抽出,如果释放了区块,则由配置器回收free-list)
1、空间配置函数allocate()实现
上述如果大于了128就直接调用第一级配置器,否则申请内存池分配,具体的示意图如下:
1)首先根据需要申请的大小n,找到对应的free_list中的位置,FREELIST_INDEX实现了内存对齐。
2)将当前区块指针赋值result
3)将当前free_list已分配区块抽出,并返回客端result
2、空间释放函数deallocate()实现
代码略(详见《STL源码剖析》),具体流程为:
1)大于128则直接调用第一级配置器的释放函数
2)根据释放的内存大小n,找到对应的free_list
3)回收区块,调整free_list
示意图如下:
3、重新填充free lists
如果free_list中没有可用区块时,则调用refill(),为free list重新填充空间,新的空间将取自内存池(由chunk_alloc完成),默认情况下取得20个新节点(新区块)。具体流程为:
1)char *chunk = chunk_alloc(n, nobjs),调用分配函数,nobjs为20,以引用方式传递;
2)调用free list,放入新节点
4、chunk_alloc内存池分配
具体流程为:
1)计算内存池剩余内存空间
2)如果内存池剩余空间完全满足需求量,则修改内存池剩余空间,并返回所需分配的内存;
3)如果内存池剩余空间不能完全满足需求量,但足够供应一个以上的区块,则计算能够分配的区块,修改内存池剩余空间,并返回所需分配的内存;
4)如果内存池剩余空间连一个区块都不能提供。调用malloc分配新的内存来补充内存池,如果操作系统都没有,无法malloc新内存,则调用第一级配置器释放内存并再次尝试分配
5)上述都无法成功分配,发出bad_alloc异常
流程图如下: