SGI Allocator内存管理
Posted printfnothing
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SGI Allocator内存管理相关的知识,希望对你有一定的参考价值。
接上一篇博文继续讲。
我们来看第二级配置器__default_alloc_template.
先看它有哪些成员(代码参考自《STL源码剖析》):
template<bool threads,int inst>
class __default_alloc_template
private:
/*将bytes上调至__ALIGN(默认为8)的倍数*/
static size_t ROUND_UP(size_t bytes)
return ((bytes+__ALIGN-1)&~(__ALIGN-1));
/*管理的内存块*/
union obj
union obj* free_list_link;
char client_data[1];
static obj* volatile free_list[__NFREELISTS]; //空闲内存链表
/*根据bytes大小定位到free_list*/
static size_t FREELIST_INDEX(size_t bytes)
return ((bytes+__ALIGN-1)/__ALIGN-1);
/*返回一个大小为n的对象,并可能加入大小为n的其他区块到free_list相应位置*/
static void*refill(size_t n);
/*从内存池或者系统申请nobjs个大小为size的内存块*/
static char*chunk_alloc(size_t size,int &nobjs);
static char*start_free; //内存池起始位置
static char*end_free; //内存池结束位置
static size_t heap_size; //内存池总内存大小
public:
static void*allocate(size_t); //分配内存
static void deallocate(void*,size_t); //回收内存
static void*reallocate(void*,size_t,size_t); //重配置内存
与第一级配置器相比,__default_alloc_template的内存管理更为复杂。
策略:
1.__default_alloc_template只管理一定大小的内存块(__MAX_BYTES默认为128B),当用户程序向它申请、释放或者重配置超过该大小的内存时,__default_alloc_template的相关成员函数会调用第一级配置器的相关成员函数进行处理。
这里要提一下union obj这个指向内存块的联合体,刚开始读源码的时候,我一直迷惑不解这个联合体是如何指向不同大小的内存块的。起初我以为obj就是用来分配的内存,后来发现不对,按照obj的定义,它包含一个obj*指针和一个包含一个字符的字符数组,根据联合体的性质,sizeof(obj) = 4,如果obj就是分配给用户的内存,那obj只有4B这么大。我又仔细读了一遍书上关于obj的说明:
由于union之故,从其第一字段观之,obj可视为一个指针,指向相同形式的另一个obj;从第二个字段观之,obj可被视为一个指针,指向实际区块。
从书上这句话来看,obj应该被当作指向一块实际内存的指针,再结合obj的目的(减少额外内存开销)obj实际作用应该是当某块内存处于free_list中时,obj中的free_list_link起作用——链接下一块obj,当该内存分配出去,即从free_list删除后obj中的client_data起作用,指向该块内存。所以最后一个问题,client_data如何起到作用?由于书上没有给出obj具体使用实例,这里我补充一个:
union obj* ob = (union obj*)::operator new(100);
cout<<"(*ob)'s address:"<<ob<<endl;
cout<<"ob->client_data:"<<(void*)ob->client_data<<endl;
现在很清楚了,obj存放某一块内存的首地址,由于union之故,该内存首址即client_data数组的首地址,所以client_data可以指向不同大小的内存。(ob=&obj=client_data)
2.__default_alloc_template如何管理小于128B的内存块?
以内存分配为例:
1)调用allocate(n)进行内存分配;
2)n>__MAX_BYTES,调用一级配置器进行分配:
if(n>__MAX_BYTES)
return(malloc_alloc::allocate(n));
3)否则,从free_list相应位置找到一块内存返回:
my_free_list = free_list+FREELIST_INDEX(n);
result = *my_free_list;
到了这一步可能会出现问题,上一篇博文讲到SGI空间配置器要解决内存不够用的问题。内存不足表现在两个方面:
(1) 用户程序向free_list申请内存时,free_list内存不足;
(2) free_list向内存池__default_alloc_template申请内存时,__default_alloc_template内存不足。
4)先看__default_alloc_template如何解决问题(1):
如果result!=0,即free_list有相应大小的内存块可以分配,则将内存块从free_list中直接删除,并返回result;
否则,allocate函数转而调用refill函数,refill函数的功能是free_list向内存池剩余空间申请若干块相同大小的内存块并挂载到free_list相应位置。
refill函数部分代码如下(具体代码见《STL源码剖析》):
void* refill(size_t n)
int nobjs = 20; //默认向内存池申请20个大小为n B的内存块
char* chunk = chunk_alloc(n,nobjs); //向内存池申请nobjs块大小为n B的内存块,也有可//能申请到小于nobjs块,具体见后面chunk_alloc函//数介绍
……
if(1==nobjs) return chunk; //如果只申请到一块n B大小的内存,则直接返回该内存
…..
/*如果申请到的内存块多余一块,则将除第一块以外的所有内存块链接到free_list相应
位置上*/
result =(obj*)chunk;
……
for(i=1;;i++)
/*将各个内存块串连起来*/
…
return result; //返回第一块内存
5)最后看free_list向内存池申请内存的函数chunk_alloc:
char* chunk_alloc(size_t size,int&nobjs)
char* result;
size_t total_bytes = size*nobjs;
size_t bytes_left = end_free-start_free; //内存池剩余空间
/*内存池内存充足*/
if(bytes_left>=total_bytes)
result = start_free;
start_free +=total_bytes;
return result;
/*内存池内存不够分配size*nobjs大小内存,但是至少可以分配一块size大小的内存*/
else if(bytes_left>=size)
nobjs = bytes_left/size; //nobjs变为实际分配的size大小内存块的块数
total_bytes = size*nobjs;
result = start_free;
start_free +=total_bytes;
return result;
/*内存池剩余空间不足size*/
else
/*计算内存池需要向系统申请多少内存*/
size_t bytes_to_get = 2*total_bytes+ROUND_UP(heap_size>>4);
/*如果内存池还有不足size的剩余空间*/
if(bytes_left>0)
/*将内存池这些剩余内存全部分配给free_list,这步操作完成后,内存池已无内存可用*/
/*此时内存池需要补充新鲜血液,要向系统申请内存*/
start_free = (char*)malloc(bytes_to_get);
/*如果系统也没有充足的内存*/
if(start_free==0)
……
/*此时要从掌管size大小的free_list开始往后遍历一遍free_list*/
for(i =size;i<=__Max_BYTES;i+=__ALIGN)
….
/*如果某一free_list上有空闲内存,则将其交还给内存池,然后再递归调用此函数,分配内存,修正nobjs*/
….
return chunk_alloc(size,nobjs);
/*到了这一步,无论是内存池还是free_list还是系统都没有大于等于size的内存块可用,那么只好请求第一级配置支援咯*/
end_free = 0;
start_free = (char*)malloc_alloc::allocate(bytes_to_get);
/*系统分配内存池以bytes_to_get大小的内存块,内存池修改相关数据,然后递归调用chunk_alloc*/
heap_size +=byte_to_get;
end_free = start_free+bytes_to_get;
return chunk_alloc(size,nobjs);
总结起来,chunk_alloc函数目的就是从__default_alloc_template或者它的free_list或者第一级配置器或者系统堆中至少找到一块满足size大小的内存块交给refill,否则抛出异常。
至此,SGI两级空间配置器空间管理策略介绍完了,感叹下,STL博大精深,一个空间配置器都写的如此精髓,看来学习还要加把劲了。
以上是关于SGI Allocator内存管理的主要内容,如果未能解决你的问题,请参考以下文章