stl 空间配置器理解

理解了一下stl的空间配置器,发现一个比较好的学习方法,跟着代码自己也跟着写一遍,顺便加些注释,可以更加帮助自己理解。

如new,delete一般,分为两个步骤,1,配置空间,2,构造对象(1,析构对象,2,释放空间)

一。构造和析构的基本工具(construct,destroy)

1,construct(构造)

template<class T1,class T2>
inline void construct(T1 * p,const T2 & value){
    new (p) T1(value);       //在T1类型的p处用T2类型的value进行初始化
}

2,destroy(析构)

析构有两个版本

   1,第一个版本,接收一个对象指针,调用其析构函数

//1
template<class T> inline void destroy(T * pointer){ pointer->~T(); }

  2,第二个版本,接受一个迭代器区间

//2
template<class ForwardIterator> inline void destroy(ForwardIterator first,ForwardIterator last){ _destroy(first,last,value_type(first)); } template<class ForwardIterator,class T> inline void _destroy(ForwardIterator first,ForwardIterator last,T *){ typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor; _destroy_aux(first,last,trivial_destructor()); } //2.1 template<class ForwardIterator> inline void _destroy_aux(ForwardIterator first,ForwardIterator last,__false_type){ for(;first < last ;first++) destroy(&*first); } //2.2 template<class ForwardIterator> inline void _destroy_aux(ForwardIterator first,ForwardIterator last,__true_type){ // trival type : do nothing }

判断元素的数值型别是否是trivial destructor,不重要的类型,如果依次对每个做析构,很浪费效率,所以对于其不重要的类型,就用不着做什么了,do nothing

为了针对原生指针(char * ,wchar_t*两种)还有其特化版本,同样此也是不重要(trivial)的类型,不用做什么了

//3 特化
inline void destroy(char * ,char * ){}
//4 特化
inline void destroy(wchar_t * ,wchar_t *){}

 二。空间的配置和释放

    空间的配置和释放分为两级,第一级配置器仅是对于malloc和free的封装,并没有对效率的强化。因此有了第二级配置器,用了内存池的思想,提高了效率

  1,第一级配置器

#if 0
    #include <new>
    #define __THROW_BAD_ALLOC throw bad_alloc
#elif !define(__THROW_BAD_ALLOC)
    #include <iostream.h>
    #define __THROW_BAD_ALLOC cerr<<"out of memory"<<endl;exit(1);
#endif

template<class inst>
class __malloc_alloc_template{
private:
    //oom : out of memory ,these funcs to handle oom
    static void * oom_malloc(size_t);
    static void * oom_realloc(void * ,size_t);
    static void (* __malloc_alloc_oom_handler)();   //在oom_xxx中调用处理malloc失败的情况
public:
    static void * allocate(size_t n){
        void *result = malloc(n);
        if( 0 == result)
            result = oom_malloc(n);
        return result;
    }
    static void deallocate(void * p,size_t){
        free(p);
    }
    static void * reallocate(void * p,size_t ,size_t new_sz){
        void * result = realloc(p,new_sz);
        if( 0 == result)
            result = oom_realloc(p,new_sz);
        return result;
    }
    static void (* set_malloc_handler(void (*f)()))(){    //配置内存失败时会调用oom_XXX,其中调用f处理例程,此处可自己设置处理例程
        void (*old)() = __malloc_alloc_oom_handler;
        __malloc_alloc_oom_handler = f;
        return old;
    }
};

template<int inst>
void (*__malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
template<int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n){
    void (* __my_malloc_handler)();
    void * result;
    for(;;){
        __my_malloc_handler = __malloc_alloc_oom_handler;
        if(0 == __my_malloc_handler) {__THROW_BAD_ALLOC}
        (*__my_malloc_handler)();
        result = malloc(n);
        if(result) return result;
    }
}
template<int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void * p ,size_t n){
    void (* __my_realloc_handle)();
    void * result;
    for(;;){
        __my_realloc_handle = __malloc_alloc_oom_handler;
        if(0 == __my_realloc_handle){__THROW_BAD_ALLOC}
        (*__my_realloc_handle)();
        result = realloc(p,n);
        if(result) return result;
    }
}

typedef __malloc_alloc_template<0> malloc_allloc;

  2,第二级配置器

   主要过程:如果分配的区块够大,大于128bytes,就移交给第一级配置器处理,当区块小于128bytes时,以内存池管理,每次配置一大块内存,并维护对应之自由链表。下次再有相同大小的内存需求,直接从内存池中在取出即可。

  二级配置器主动将所需内存调整至8的倍数。二级配置器内维护16个链表,各自管理8,16,24,。。。128大小的内存块。

    

    1.对应的free list有可用的区块,直接拿过来用。

      2.如果没有可用的区块,调用函数refill()为free list重新填充空间。

  refill的工作:从内存池中取得内存,并依次分割成相应大小的块交给相应的链表维护,默认再取得20个区块,但实际情况还要在chunk_alloc中判断,可能不能完全取到20个区块。

  在refill中调用chunk_alloc取得空间;

  chunk_alloc中分为三种情况,

    1.内存池中的内存容量完全够需求

    2.内存池剩余空间不能完全满足需求,但足够供应一个以上的区块

    3.连一个区块都不能获得

         3.1.先将剩余的一些小空间交于其他的链表管理,省的浪费

         3.2.从heap中分配空间补充内存量,补充内存池。

         3.3.补充内存池之后,调整start_free,end_free,递归调用chunk_alloc来继续分配空间。

enum {__ALIGN = 8};
enum {__MAX_BYTES = 128};
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};
template<bool threads,int inst>
class __default_alloc_template{
private:
    static size_t ROUND_UP(size_t bytes){   //调整至8的倍数
        return ((bytes + __ALIGN -1) & ~(__ALIGN-1));
    }
private:
    union obj{
        union obj * free_list_link;
        char client_data[1];
    };
private:
    static obj * volatile free_list[__NFREELISTS];
    static size_t FREELIST_INDEX(size_t bytes){   //决定使用第几号free_list
        return (bytes + __ALIGN -1)/__ALIGN - 1;
    }
    //返回一个大小为n的对象,并可能将此加入到free_list中
    static void * refill(size_t n);
    //配置空间,内存池
    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 n);
    static void deallocate(void * p,size_t n);
    static void * reallocate(void * p,size_t oldsz,size_t newsz);
};

template<bool threads,int inst>
char * __default_alloc_template<threads,inst>::start_free = 0;
template<bool threads,int inst>
char * __default_alloc_template<threads,inst>::end_free = 0;
template<bool threads,int inst>
size_t __default_alloc_template<threads,inst>::heap_size = 0;

template<bool threads,int inst>
__default_alloc_template<threads,inst>::obj * volatile
__default_alloc_template<threads,inst>::free_list[__NFREELISTS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

//1 首先判断区块大小,若大于128调用第一级配置器
//2 小于128,从free_list中取(取之前是对调整后的区块进行取),有则取,没有则将区块调整至8的倍数,调用refill填充空间
template<bool threads,int inst>
void * __default_alloc_template<threads,inst>::allocate(size_t n){
    obj * volatile * my_free_list;
    obj * result;
    if( n > (size_t)__MAX_BYTES)
        return (malloc_allloc::allocate(n));

    my_free_list = free_list+FREELIST_INDEX(n);
    result = * my_free_list;
    if(result == 0){     //例:当内存池中96bytes的区块取光后,就refill重新建立
        void * r = refill(ROUND_UP(n);
        return r;
    }
    *my_free_list = result->free_list_link;
    return result;
}

template<bool threads,int inst>
void __default_alloc_template<threads,inst>::deallocate(void * p,size_t n){
    obj * q = (obj *) p;
    obj * volatile * my_free_list;
    if(n > __MAX_BYTES){
        malloc_allloc::deallocate(p,n);
        return;
    }
    
    my_free_list = free_list+FREELIST_INDEX(n);
    q->free_list_link = *my_free_list;
    *my_free_list = q;
}

template<bool threads,int inst>
void * __default_alloc_template<threads,inst>::refill(size_t n){
    int nobjs = 20;
    char * chunk = chunk_alloc(n,nobjs); //默认申请20个块
    obj * volatile * my_free_list;
    obj * result;
    obj * current_obj,*nextobj;
    int i;
    if(1 == nobjs)
        return chunk;

    my_free_list = free_list + FREELIST_INDEX(n);
    
    result = (obj * )chunk;  //等待返回给需求者
    * my_free_list = nextobj = (obj * )(chunk + n);
    for(i = 1;;i++){
        current_obj = nextobj;
        nextobj = (obj * )((char *)nextobj+n); //不断进行分割
        if(nobjs - 1 == i){  //分割结束
            current_obj->free_link_list = 0;
            break;
        }
        else
            current_obj->free_link_list = nextobj;
    }
    return result;
}

template<bool threads,int inst>
char * __default_alloc_template::chunk_alloc(size_t size,int & nobjs){
    char * result;
    size_t total_bytes = size * nobjs;
    size_t bytesleft = end_free - start_free;
    
    //1内存池剩余空间完全满足需求量
    if(bytesleft >= total_bytes){
        result = start_free;
        start_free+=total_bytes; //提供区块后,调整start_free
        return result;
    }
    //2 不能完全满足需求量,但能供应一个及以上
    else if(bytesleft >= size){
        nobjs = bytesleft/size;
        total_bytes = size*nobjs;
        result = start_free;
        start_free+=total_bytes;
        return result;
    }
    //3 连一个也不能提供
    else{
        size_t bytes_to_get = 2*total_bytes + ROUND_UP(heap_size>>4);
        if(bytesleft > 0){       //将剩余的一小点空间配给别的list
            obj * volatile * my_free_list = free_list+FREELIST_INDEX(bytesleft);
            ((obj*)start_free)->free_list_link = *my_free_list;
            *my_free_list = (obj *)start_free;       //如果不是跟
        }
        start_free = (char *)malloc_allloc(bytes_to_get);
        if(0 == start_free){
            int i;
            obj * volatile * my_free_list,*p;

            for(i = size;i<__MAX_BYTES;i+=__ALIGN){
                my_free_list = free_list+FREELIST_INDEX(i);
                p = *my_free_list;
                if(0!=p){
                    *my_free_list = p->free_list_link;
                    start_free = (char * )p;
                    end_free = start_free+i;
                    //递归调用自己,为了修正nobjs
                    return chunk_alloc(size,nobjs);   //调整了start_free和end_free了,就再次进行分配,至少能分到一个区块了
                }
            }
            end_free = 0;
            start_free = (char * )malloc_allloc::allocate(bytes_to_get);
        }
        heap_size += bytes_to_get;
        end_free = start_free+bytes_to_get;
        return chunk_alloc(size,nobjs);   //调整了start_free和end_free了,就再次进行分配,至少能分到一个区块了
    }

}

 

posted @ 2015-08-16 20:14  朽木可雕否  阅读(371)  评论(0编辑  收藏  举报