stl第二级空间配置器详解(1)

       SGI STL考虑到小型内存区块的碎片问题,设计了双层级配置器,第一级配置直接使用malloc()和free();第二级配置器则视状况采用不一样的策略,当配置区大于128bytes时,直接调用第一级配置器;当配置区块小于128bytes时,遍不借助第一级配置器,而使用一个memory pool来实现。到底是使用第一级配置器仍是第二级配置器,由一个宏定义来控制。SGI中默认使用第二级配置器。函数

       第一级配置器实现的比较简单,调用malloc()申请内存,申请失败的时候,将抛出bad_alloc异常。下边我着重介绍下二级配置器的实现思路。spa

      上边提到了第二级配置器视状况不一样而采用不一样的策略;其主要目的是避免太多小额区块形成内存的碎片。一般状况下,当配置区大于128bytes时,配置器视之为“足够大”而直接调用一级配置器,当配置区块小于128bytes时,就之内存池来管理,具体作法是:设计

        sgi二级配置器会将任何小额区块的内存需求量上调至8的倍数,(例如需求是30bytes,则自动调整为32bytes),而且在它内部会维护16个free-list, 各自管理大小分别为8, 16, 24,…,128bytes的小额区块,这样当有小额内存配置需求时,直接从对应的free list中拔出对应大小的内存(8的倍数);当客户端归还内存时,将根据归还内存块的大小,将须要归还的内存插入到对应free list的最顶端。以下图:指针

        

                                                                                                                    图1对象

      那么什么是free-list呢?blog

      首先咱们看下free-list的定义内存

      上边free-list节点定义得很是巧妙,与普通链表节点采用struct不一样,这里采用的是union;主要缘由是因为为了维护free-list链表,每一个节点须要额外的指针(指向下一个节点),这样就会形成一种内存浪费。为了不这种负担,因此采用的union,根据union的特色,从第一个字段看,obj能够看做一个指针,指向链表中的下一个节点;从第二个字段看,obj也能够视为一个指针,不过是指向实际的内存区,如图1所示,这种一物两用的结果,就是不会为了维护链表所必须的指针而形成内存浪费。(由于stl容器是保存对象的,因此其自身信息固然要求是尽量的少占用内存)。io

      下边咱们根据一个图来说下第二级空间配置器分配及归还内存区块的过程:class

      内存分配:thread

     

 图2

            如上,咱们要申请96bytes的内存(因为其内部有自动调整机制,因此有可能89~95bytes的申请也会自动上调到96bytes), 首先肯定须要在第几号free-list中获取,如图示是第11号free-list my_free_list,而后获取这个第一个元素(即第一块内存)的地址赋值给result,再调整my_free_list让其指向下一个区块。

           内存归还:


图3

跟配置内存相似,一样是先寻找对应的free list,而后将要归还的内存块插入到对应free list的头部。
不过在内存配置的过程当中,咱们还须要处理这种状况:当对应free list没有可用区块时,就须要给对应的free list从新填充内存。以下图:
图4 

         

//传回一个大小为 n的对象,而且有时候会为适当的freelist增长节点. 
//假设 n已经适当上调至 8的倍数。
template <bool threads, int inst> 
void* __default_alloc_template<threads, inst>::refill(size_t n) 
{ 
 int nobjs = 20; 
// 呼叫 chunk_alloc(),尝试取得 nobjs个区块作为 free list的新节点。
 // 注意参数 nobjs是pass by reference。
 char * chunk =chunk_alloc(n, nobjs);
obj * volatile * my_free_list; 
 obj * result; 
 obj * current_obj, * next_obj; 
 int i; 
第 2 章空间配置器(allocator)
// 若是只得到一个区块,这个区块就拨给呼叫者用,free list无新节点。
 if (1 == nobjs) return(chunk); 
// 不然准备调整 free list,归入新节点。
 my_free_list = free_list + FREELIST_INDEX(n); 
// 如下在 chunk空间内创建freelist
 result = (obj *)chunk; //这一块准备传回给客端
// 如下导引 free list指向新配置的空间(取自记忆池)
 *my_free_list = next_obj = (obj *)(chunk + n); 
// 如下将 free list 的各节点串接起来。
 for (i = 1; ; i++) {//从 1 开始,由于第 0 个将传回给客端
 current_obj = next_obj; 
 next_obj = (obj *)((char *)next_obj + n); 
 if (nobjs - 1 == i) { 
 current_obj -> free_list_link = 0; 
 break; 
 } else { 
 current_obj -> free_list_link = next_obj; 
 } 
 } 
 return(result); 
} 

        refill()函数完成的主要功能是:根据所须要申请的区块的大小n,调用chunk_alloc(n, nobjs); 默认申请nobjs个区块(默认为20,又可能不足20,nobjs是引用传递);若是申请的区块只有一个,那么直接返回,free list仍旧无可用区块,若是大于1,那么将第一个chunk块做为返回值,其他的chunk按照n划分为free list的节点,并将其串接到free list中区。最后free list头节点会指向到新分配的chunk。

        综上,我便将stl二级空间配置的空间的配置,回收,以及从新填充大小详细介绍了一遍,下篇将继续介绍内存池的设计。

相关文章
相关标签/搜索