zoukankan      html  css  js  c++  java
  • SGI_STL_空间配置器

    一般而言,我们习惯C++内存申请操作和释放操作如下:

    class Foo{};

    Foo * pf = new Foo;  //1.申请空间2.调用构造函数

    delete pf;                //1.析构函数2.释放空间

    但是为了精确分工,STL Allocator这两个阶段分开来。PS:以下代码不讨论多线程情况

    内存申请:alloc::allocate()负责。

    内存释放:alloc::deallocate()负责。

    对象构造:全局 ::construct()负责。

    对象析构:全局 ::destroy()负责。

    1.<stl_construct.h> 对象构造和析构关键代码

    【::construct()和::destroy()被设计为全局函数。::destory()有2个版本,第一个版本接受一个指针,调用该析构函数即可,第二个版本考虑到大范围的对象析构可能无关痛痒(trivial destructor),使用value_type()获得迭代器所指对象的型别,再利用__tyep_traits<T>判断trivial?不做:调用第一个版本】

    #include <new.h>        //为了使用placement new
    
    template <class T1, class T2>
    inline void construct(T1* p, const T2& value) 
    {
        new (p) T1(value);     // placement new; 显示调用构造函数T1(value);
    }
    
    // 以下是destroy() 第一版本,接受一个对象指针。
    template <class T>
    inline void destroy(T* pointer) 
    {
        pointer->~T();    // 析构
    }
    
    // 以下是destroy() 第二版本,接受两个迭代器,此函数找出元素得数值类型
    // 利用__type_traits<> 采用适当措施
    template <class ForwardIterator>
    inline void destroy(ForwardIterator first, ForwardIterator last) 
    {
        __destroy(first, last, value_type(first));
    }
    
    // 判断对象的value type是否有trivial destructor
    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());
    }
    
    // 如果对象的型別(value type)有non-trivial(要紧的) destructor
    //循环调用destroy一个版本
    template <class ForwardIterator>
    inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) 
    {
        for ( ; first < last; ++first)
            destroy(&*first);
    }
    
    // 如果对象的型別(value type)有trivial(无关紧要的) destructor
    template <class ForwardIterator> 
    inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) 
    {
        //不处理
    }
    
    // 以下是destroy() 第二版本針對迭代器為char* 和wchar_t* 的特化版
    inline void destroy(char*, char*) {}
    inline void destroy(wchar_t*, wchar_t*) {}

    2.<stl_alloc.h>     空间的申请和释放

    【C++内存申请基本操作:::operator new(),内存释放基本操作:::operator delete()。SGI用malloc和free这2个C函数代替完成内存的申请和释放。考虑到内存碎片问题,SGI设计了两级配置器,第一级直接使用malloc和free,包括了内存不足处理机制,需要调用者自己提供。第二级判断配置区超过128字节,调用第一级配置器。小于128字节,采用内存池管理】

    2.1 配置器接口

    //简单的转调用,可能第一级或者第二级配置器

    template<class T, class Alloc>
    class simple_alloc 
    {
    public:
        static T *allocate(size_t n)
        { 
            return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); 
        }
        static T *allocate(void)
        { 
            return (T*) Alloc::allocate(sizeof (T)); 
        }
        static void deallocate(T *p, size_t n)
        { 
            if (0 != n) Alloc::deallocate(p, n * sizeof (T)); 
        }
        static void deallocate(T *p)
        { 
            Alloc::deallocate(p, sizeof (T)); 
        }
    };

    2.2 vector简单使用例子

    template<class T, class Alloc>
    class vector
    {
    protected:
        typedef simple_alloc<value_type,Alloc> data_allocator;
    
        void deallocate()
        {
            if(...)
                data_allocator::deallocate(start,end_of_storage - start);
        }
    };

    2.3 第一级配置器

    【SGI第一级配置器的allocate和realloc都是在调用C函数malloc和realloc不成功后,改调用oom_malloc和oom_realloc,后两者都有内循环,不断调用你指定的内存不足处理函数】

    // Malloc-based allocator.  Typically slower than default alloc below.
    // Typically thread-safe and more storage efficient.
    #ifdef __STL_STATIC_TEMPLATE_MEMBER_BUG
    # ifdef __DECLARE_GLOBALS_HERE
    void (* __malloc_alloc_oom_handler)() = 0;
    // g++ 2.7.2 does not handle static template data members.
    # else
    extern void (* __malloc_alloc_oom_handler)();
    # endif
    #endif
    
    //inst没排上用场
    template <int inst>
    class __malloc_alloc_template 
    {
    private:
    
        //处理内存不足
        static void *oom_malloc(size_t);
        static void *oom_realloc(void *, size_t);
    #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
        static void (* __malloc_alloc_oom_handler)();
    #endif
    
    public:
        
        static void * allocate(size_t n)
        {
            //第一级直接用malloc;
            void *result = malloc(n);    
            //malloc内存不足补救函数
            if (0 == result)
                result = oom_malloc(n);
            return result;
        }
    
        static void deallocate(void *p, size_t /* n */)
        {    
            //直接free
            free(p);
        }
    
        static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
        {
            //直接使用realloc 下面为补救函数
            void * result = realloc(p, new_sz);
            if (0 == result) 
                result = oom_realloc(p, new_sz);
            return result;
        }
    
        static void (* set_malloc_handler(void (*f)()))()
        {
            //指定你的out-of-memory处理函数
            void (* old)() = __malloc_alloc_oom_handler;
            __malloc_alloc_oom_handler = f;
            return(old);
        }
    };
    
    // malloc_alloc out-of-memory handling
    
    #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
    template <int inst>
    //你的out-of-memory处理函数初始为
    void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
    #endif
    
    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_malloc_handler)();
        void *result;
    
        for (;;) 
        {
            //同上
            my_malloc_handler = __malloc_alloc_oom_handler;
            if (0 == my_malloc_handler) 
                { __THROW_BAD_ALLOC; }
            (*my_malloc_handler)();
            result = realloc(p, n);
            if (result)
                return(result);
        }
    }

    2.4 第二级配置器

    【每次先申请一大块内存作为内存池,并且维护16条链表,分别从8-16-24-32……-128字节(配置器会主动将当前内存申请提升到8的倍数),链表负责配置出去(配置出去时链表不再指向)和回收回来(回收回来时链表重新指向),当链表空间不足时向内存池申请。】

     

    2.4.1 链表节点结构

    //利用union的性质使得链表既是指针又是实际区块
    union obj
    {
        union obj * free_list_link;
        char client_data[1];       //柔性数组
    };

     

    2.4.2 配置器类内代码

    enum {__ALIGN = 8};
    enum {__MAX_BYTES = 128};
    enum {__NFREELISTS = __MAX_BYTES/__ALIGN};
    #endif
    
    template <bool threads, int inst>
    class __default_alloc_template {
    
    private:
        //扩展到__ALIGN的倍数也即是的倍数
        static size_t ROUND_UP(size_t bytes) 
        {
            return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
        }
    private:
        union obj
        {
            union obj * free_list_link;
            char client_data[1];    /* The client sees this.        */
        };
    private:
        //16条链表
        static obj *  free_list[__NFREELISTS]; 
        
        //根据申请的大小快速找到所属链表
        static  size_t FREELIST_INDEX(size_t bytes) 
        {
            return (((bytes) + __ALIGN-1)/__ALIGN - 1);
        }
        
        //向内存池申请n大小的区间n为的倍数
        static void *refill(size_t n);
    
        //内存池成处理申请函数一般为一次性配置nobjs个size大小的块nobjs默认为
        //不足时,nobjs返回不定数目
        static char *chunk_alloc(size_t size, int &nobjs);
    
        //内存池起始位置只在chunk_alloc变化
        static char *start_free;
        //内存池结束位置只在chunk_alloc变化
        static char *end_free;
        //内存池heap_size
        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 old_sz, size_t new_sz);
    
    } ;

    2.4.3 allocate()实现

    /* n must be > 0      */
    static void * allocate(size_t n)
    {
        obj * __VOLATILE * my_free_list;
        obj * __RESTRICT result;
    
        //大于字节就调用第一级配置器
        if (n > (size_t) __MAX_BYTES) 
        {
            return(malloc_alloc::allocate(n));
        }
    
        //找到链表中的所属链表
        my_free_list = free_list + FREELIST_INDEX(n);
        
        result = *my_free_list;
        //链表没有可用的块调用refill申请
        if (result == 0)
        {
            void *r = refill(ROUND_UP(n));
            return r;
        }
        //调整链表不再指向这块被使用的块
        *my_free_list = result -> free_list_link;
        return (result);
    };

    2.4.4 deallocate()实现

    /* p may not be 0 */
    static void deallocate(void *p, size_t n)
    {
        obj *q = (obj *)p;
        obj * __VOLATILE * my_free_list;
        
        //大于使用第一级配置器
        if (n > (size_t) __MAX_BYTES) 
        {
            malloc_alloc::deallocate(p, n);
            return;
        }
        //找到所属链表
        my_free_list = free_list + FREELIST_INDEX(n);
        
        //调整链表回收区块
        q -> free_list_link = *my_free_list;
        //链表重新指向回收块
        *my_free_list = q;
    
    }

    2.4.5 refill()实现

    void* __default_alloc_template<threads, inst>::refill(size_t n)
    {
        //默认申请块数
        int nobjs = 20;
        //nobjs为传引用
        char * chunk = chunk_alloc(n, nobjs);
        obj * __VOLATILE * my_free_list;
        obj * result;
        obj * current_obj, * next_obj;
        int i;
        
        //只有一块返回给调用者链表无新节点
        if (1 == nobjs) 
            return(chunk);
    
        //不止一块找到所属链表
        my_free_list = free_list + FREELIST_INDEX(n);
    
        //对新的区块建链表
        result = (obj *)chunk;
        //链表指向从内存池拿出的链表
        *my_free_list = next_obj = (obj *)(chunk + n);
        //将新链表之间的节点串接起来
        for (i = 1; ; i++)  //从开始第个返回给申请者
        {
            //当前节点
            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);   //返回给申请者
    }

    2.4.6 内存池chunk_alloc()实现

    _default_alloc_template<threads, inst>::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
            result = start_free;
            //内存池可用起始地址+=申请总量
            start_free += total_bytes;
            return(result);
        }
        //不足以满足申请总量但是可以供应一个以上区块
        else if (bytes_left >= size) 
        {
            //重新得到能够供应个数
            nobjs = bytes_left/size;
            //重新计算总量
            total_bytes = size * nobjs;
            //同上
            result = start_free;
            start_free += total_bytes;
            return(result);
        }
        //一个区块都提供不了
        else 
        {
    
            size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);
    
            //余额大于把这些零头凑到链表里去
            if (bytes_left > 0) 
            {
                obj * __VOLATILE * my_free_list = free_list + FREELIST_INDEX(bytes_left);
        
                ((obj *)start_free) -> free_list_link = *my_free_list;
                *my_free_list = (obj *)start_free;
            }
            //申请heap空间,补充内存池
            start_free = (char *)malloc(bytes_to_get);
            if (0 == start_free) 
            {
                int i;
                obj * __VOLATILE * my_free_list, *p;
                // Try to make do with what we have.  That can't
                // hurt.  We do not try smaller requests, since that tends
                // to result in disaster on multi-process machines.
    
                //以下搜索适当的链表
                //适当是指"尚有未用区块的链表"
                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;
                        //再一次调用自身
                        return(chunk_alloc(size, nobjs));
                        // Any leftover piece will eventually make it to the
                        // right free list.
                    }
                }
                //没办法没内存用了
                end_free = 0;    // In case of exception.
                //调用第一级配置器看看out-of-memory机制能否出点力
                start_free = (char *)malloc_alloc::allocate(bytes_to_get);
                // This should either throw an exception
                // or remedy the situation.  Thus we assume it
                // succeeded.
            }
            //heap_size+=
            heap_size += bytes_to_get;
            //结束位置重新界定
            end_free = start_free + bytes_to_get;
            return(chunk_alloc(size, nobjs));
        }
    }

     

     

  • 相关阅读:
    SystemVerilog搭建测试平台---第一章:验证导论
    二线制I2C CMOS串行EEPROM续
    二线制I2C CMOS串行EEPROM
    Codeforces 777E:Hanoi Factory(贪心)
    2019HPU-ICPC-Training-1
    Codeforces 777B:Game of Credit Cards(贪心)
    Codeforces 777D:Cloud of Hashtags(暴力,水题)
    Codeforces 777C:Alyona and Spreadsheet(预处理)
    Codeforces 888D: Almost Identity Permutations(错排公式,组合数)
    Codeforces 888E:Maximum Subsequence(枚举,二分)
  • 原文地址:https://www.cnblogs.com/guyan/p/2678606.html
Copyright © 2011-2022 走看看