zoukankan      html  css  js  c++  java
  • SGI的特殊空间配置器

    SGI的空间配置器allocator只是简单的new和delete的一层包装,没有提供效率的强化。

    而一般C++内存配置和释放操作如下:

    class  Foo  { ... }  

    Foo  *pf = new Foo;

    delete pf; 

    new算式:1)使用new配置内存,2)使用Foo构造对象

    delelte算式: 1)使用~Foo()将对象析构 ,2)使用delete释放内存

         STL allocator 将这两阶段操作区分开来。内存配置操作由 alloc::allocate() 负责,内存释放操作由 alloc::deallocate() 负责对象构造操作由 ::construct() 负责,对象析构操作由 ::destroy() 负责

        如下图是STL allocator的构造:

                                    | <stl_constuct.h>   //全局函数:constrcut(),destory()负责对象的构造和析构

            <memory>----- |<stl_alloc.h>         // 一、二级配置器(alloc)

                                    |<stl_uninitialized.h> //全局函数:un_initialized_copy()、un_initialized_fill()、un_initialized_fill_n() 复制填充大块内存数据

                                                                   //这些函数会考虑效率,最差用constrcut(),最佳时用memmove()直接进行内存移动

                                                                    

    一、<stl_constuct.h>

    constrcut()

    template<class T1,class T2>
    inline void construct(T1*p,const T2&value )
    {
       new (p) T1(value);
    }

    destory()

    template<class T>//版本一
    inline void destroy(T* pointer)
    {
       pointer->~T();
    }
    
    template<class ForwardIterator,class T>//版本二
    inline void destroy(ForwardIterator first,ForwardIterator last,T*)
    {
       typedef  typename _type_traits<T>::has_trivial_destructor tivial_destructor();
    _destory_aux(first,last,trivial_destructor);
    }

    版本二会根据数值型别判断,如果是_true_type就什么也不做, _false_type会循环每个对象依次调用版本一的destroy

    二、<stl_alloc.h> 

    SGI对此的设计哲学如下:

            1. 向 system heap 要求空间。

            2. 考虑多线程(multi-threads)状态。

            3. 考虑内存不足时的应变措施。

            4. 考虑过多“小型区块”可能造成的内存碎片(fragment)问题。

    为了处理小型区块可能造成的内存碎块,SGI设计两层配置器,如下:

    第一级:   (大于128bytes)                                                                                                    第二级

    template <int inst>                                                                                       template<bool threads,int inst>

    class _malloc_alloc_template{..}                                                                      class _default_alloc_template{..};

    其中:                                                                                                              其中:  

    1、allocate()直接使用malloc()                                                                          1、维护16个 free lists,负责16种小型区块的次配置能力

    deallocate()直接使用free()                                                                               内存池(memory pool)以malloc配置而得,内存不足转第一级配置器 

    2、模拟C++的set_new_handler()处理内存不足                                                  2、需求区块大于128bytes,转第一级配置器

           由于第一级,第二级只是以128bytes判断,所以需要用simple_alloc接口进行封装,使得由bytes转为元素大小。

         下面看看第一级配置器的内存不足处理

    template <int inst>  
    class __malloc_alloc_template { 
     private://处理内存不足函数
    static void *oom_malloc(size_t);  
    static void *oom_realloc(void *, size_t);  
     static void (* __malloc_alloc_oom_handler)();
    
    static void (* set_malloc_handler(void (*f)()))()  
    {  
        void (* old)() = __malloc_alloc_oom_handler;  
        __malloc_alloc_oom_handler = f;  
        return(old);  
    }  
    }
    
    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);  
        }  
    }  

        第二级配置器:

        当区块小于128bytes,每次配置一大块内存,并维护其对应的自由链表,配置器负责分配与回收。

         free-lists节点结构:  

          为了尽最大可能减少内存的使用, 这里使用一个union ,如果使用第一个成员, 则指向另一个相同的union obj ,如果使用第二个成员, 则指向实际的内存区域 ,这样就实现了链表结点只使用一个指针的大小空间, 却能同时做索引和指向内存区域!!!!!

    union obj {  
          union obj * free_list_link;  
          char client_data[1];    /* The client sees this. */  
    };  

      第二级配置器中主要有

    ROUND_UP(x):负责将输入的字节数上调至8的倍数,如下:

                    (x+7)&~7  // +7是上升一个8的倍数,然后~7是清除/8的余数

     allocate():  首先判断区块大小,大于128就调用第一级,小于128时就检查 free list, free list里有可用的区块就直接用,若没有就将区块大小上升至8的倍数,然后用refill()为 free list重新填充空间。如下:

    static void * allocate(size_t n)  
    {  
        obj * __VOLATILE * my_free_list;  
        obj * __RESTRICT result;  
      
        // 大于128 就调用第一级配置器  
        if (n > (size_t) __MAX_BYTES) {  
            return(malloc_alloc::allocate(n));  
        }  
        // 寻找 16 个free lists中适当的一个  
        my_free_list = free_list + FREELIST_INDEX(n);  
        #ifndef _NOTHREADS  
        /*REFERENCED*/  
        lock lock_instance;  
        #endif  
        result = *my_free_list;  
        if (result == 0) {  
            // 没找到可用的 free list,准备重新填充 free list  
            void *r = refill(ROUND_UP(n));  
            return r;  
        }  
        // 调整 free list  
        *my_free_list = result -> free_list_link;  
        return (result);  
    };
    my_free_list = free_list + FREELIST_INDEX(n);  //my_free_list会定位到数组16个元素的某一个(里面存的是对应链表头的地址)

    result = *my_free_list; //result 是一个指针,会指向对应的链表头

    deallocate(): 首先判断区块大小,大余128就调用第一级配置器,小于128就找相应的freelist,将区块回收
    static void deallocate(void *p, size_t n)  //p是待回收的内存地址,n是对应freelist 要回收的地方
    {  
        obj *q = (obj *)p;  
            obj * __VOLATILE * my_free_list;  
      
        // 大于 128 就调用第一级配置器  
             if (n > (size_t) __MAX_BYTES) {  
                    malloc_alloc::deallocate(p, n);  
                return;  
            }  
      
        // 寻找对应的 free list  
            my_free_list = free_list + FREELIST_INDEX(n);  
            // acquire lock  
            #ifndef _NOTHREADS  
            /*REFERENCED*/  
            lock lock_instance;  
            #endif /* _NOTHREADS */  
      
        // 调整 free list,回收区块  
            q -> free_list_link = *my_free_list;  
            *my_free_list = q;  //把这段待放的内存块插入链表头
            // lock is released here  
    } 
    
    

    refill():发现freelist没有可用空间时,从内存池(chunk_alloc()完成),缺省取20,内存池不足时,可能小于20.

    // 返回一个大小为 n 的对象,并且有时候会为适当的 free list 增加节点  
    // 假设 n 已经适当上调至 8 的倍数  
    /* We hold the allocation lock. */  
    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;  
      
        // 如果只获得一个区块,这个区块就分配给调用者用,free list无新节点  
        if (1 == nobjs) return(chunk);  
        // 否则准备调整 free list,纳入新节点  
        my_free_list = free_list + FREELIST_INDEX(n);  
      
        // 以下在 chunk 空间内建立 free list  
        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);  
    }  
     

     chunk_alloc():是从内存池取内存给freelist的。如果有足够块数就直接返回,不足时就返回整数块数。如果连一块都不足时,首先考虑将内存池剩余空间编入freelist,然后向堆栈申请空间,连堆栈都没有空间时,再回freelist里找没用到且区块够大的,最后还不行才抛异常。

    // 假设 size 已经适当上调至 8 的倍数  
    // 注意参数 nobjs 是 pass by reference  
    template <bool threads, int inst>  
    char*  
    __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 = 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);  
            // 以下试着让内存池中的残余零头还有利用价值(零头也应该是 8 的倍数)  
            if (bytes_left > 0) {  
                // 内存池内还有一些零头,先配给适当的free list  
                // 首先寻找适当的 free list  
                obj * __VOLATILE * my_free_list =  
                            free_list + FREELIST_INDEX(bytes_left);  
      
                // 调整 free list,将内存池中的残余空间编入  
                ((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) {  
                // heap空间不足,malloc()失败  
                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.  
                // 试着检视我们手上拥有的东西。这不会造成伤害。我们不打算尝试配置  
                // 较小的区块,因为那在多进程(multi-process)机器上容易导致灾难  
                // 以下搜寻适当的 free list  
                // 所谓适当是指"尚有未用区块,且区块够大"之 free list  
                for (i = size; i <= __MAX_BYTES; i += __ALIGN) {  
                    my_free_list = free_list + FREELIST_INDEX(i);  
                    p = *my_free_list;  
                    if (0 != p) { // free list内尚有未用区块  
                        // 调整free list以释出未用区块  
                        *my_free_list = p -> free_list_link;  
                        start_free = (char *)p;  
                        end_free = start_free + i;  
                // 递归调用自己,为了修正 nobjs  
                        return(chunk_alloc(size, nobjs));  
                // 注意,任何残余零头终将被编入适当的free-list中备用  
                    }  
                }  
      
        end_free = 0;   // 如果出现意外,到处都没内存可用  
        // 调用第一级配置器,看看 out-of-memory 机制能否尽点力  
        start_free = (char *)malloc_alloc::allocate(bytes_to_get);  
            // 这会导致抛出异常(exception),或内存不足的情况获得改善  
            }  
            heap_size += bytes_to_get;  
            end_free = start_free + bytes_to_get;  
        // 递归调用自己,为了修正 nobjs  
            return(chunk_alloc(size, nobjs));  
        }  
    }  
  • 相关阅读:
    sql server 2008 express 使用ip登陆 error:40 错误:2
    C#将Enum枚举映射到文本字符串
    Qt 自定义事件
    constexpr-C++11
    C++11 Lambda表达式(匿名函数)
    Qt5-调试器安装
    Qt5之坐标系统
    八大排序算法总结
    Qt之类反射机制
    Qt5之反射机制(内省)
  • 原文地址:https://www.cnblogs.com/daocaorenblog/p/5299346.html
Copyright © 2011-2022 走看看