• 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了,就再次进行分配,至少能分到一个区块了
        }
    
    }
  • 相关阅读:
    流行的开源分布式文件系统比较
    Linux iostat监测IO状态
    M0n0wall软件防火墙教程
    networkscripts/ifcfg配置详解
    LVM 逻辑卷管理器
    Discuz 6.0数据库结构 四(详)
    Discuz 6.0数据库结构 二(详)
    手动配置linux(centos)的IP地址
    Discuz 6.0数据库结构 五(详)
    lnk快捷方式无法打开解决方法
  • 原文地址:https://www.cnblogs.com/xiumukediao/p/4733191.html
Copyright © 2020-2023  润新知