zoukankan      html  css  js  c++  java
  • STL-空间配置器(allocator)

         STL的空间配置器作为STL六大部件的重要组成部分,它总是隐藏在一切组件的背后.它主要负责动态空间的分配、释放等管理工作。整个STL的操作对象(所有的数值)都存放在容器之内,而容器一定需要配置空间以置放资料。而这就是空间配置器(allocator)的职责了.

    一.SGI的空间配置器—std::alloc

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

       STL标准表格告诉我们,配置器定义于<memory>中,SGI<memory>内含两个文件:

      #include <stl_alloc.h>  //负责内存空间的配置与释放

      #include <stl_construct.h> //负责对象内容的构造与析构

    1.构造和析构基本工具:construct()和destroy()

    #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::T1(value);
    }
    
    //以下是destroy()第一版本,接受一个指针
    template<class T>
    inline void destroy(T* pointer){
         pointer->~T();//调用dtor ~T()
    }
    
    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_trait<T>::has_trivial_destructor trivial_destructor;
        __destroy_aux(first,last,trivial_destructor());
    }
    
    //如果元素的数值型别(value_type)有non-trivial destructor
    template <class ForwardIterator>
    inline void __destroy_aux(ForwardIterator first,ForwardIterator last,__false_type){
        for(;first<last;first++) destroy(&*first);
    }
    
    template <class ForwardIterator>
    inline void __destroy_aux(ForwardIterator first,ForwardIterator last,__true_type){}
    
    inline void destroy(char*,char*){}
    inline void destroy(wchar_t*,wchar_t*){}

      2.空间的配置与释放

         SGI是以malloc()和free()完成内存的配置与释放.考虑到小型区块所可能造成的内存破碎问题,SGI设计了双层级配置器。第一级配置器直接使用malloc()和free(),第二级配置器则视情况采用不同的策略:当配置区块超过128bytes时,视之为"足够大",便调用第一级配置器;当配置区块小于128bytes时,视之为"过小",为了降低额外负担,便采用memory pool整理方式,而不再求助于第一级配置器。

         无论alloc被定义为第一级或第二级配置器,SGI还为它再包装了一个接口,使配置器的接口更符合STL规格。

    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(n!=0) Alloc::deallocate(p,n*sizeof(T));
          }
          
          static void deallocate(T* p){
              Alloc::deallocate(p,sizeof(T));
          }
    };

    其内部四个成员函数其实都是单纯的转调用,调用传递给配置器(可能是第一级也可能是第二级)的成员函数。SGI STL容器全部都是使用这个simple_alloc接口。

      2.1 第一级配置器 __malloc_alloc_template剖析

    //注意,无"template型别参数",inst完全没有派上用场
    template<int inst>
    class __malloc_alloc_template{
    private:
          //以下都是函数指针,所代表的函数将用来处理内存不足的情况
          //oom:out of memory
          static void* oom_malloc(size_t);
          static void* oom_realloc(void*,size_t);
          static void (*__malloc_alloc_oom_handler)();
          
    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 /*n*/){
              free(p);
          }
          
          static void* reallocate(void* p,size_t old_sz,size_t new_sz){
              void* result=realloc(p,new_sz);
              if(0==result) oom_realloc(p,new_sz);
              return result;
          }
          
          //以下仿真C++的set_new_handler()
          static void (*set_malloc_handler(void (*f)()))(){
              void (*old)()=__malloc_alloc_oom_handler;
              __malloc_alloc_oom_handler=f;
              return (old);
          }
    };
    
    void (*__malloc_alloc_template<int>::__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(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);
        }
    }

    所谓c++ new handler机制是,当系统在内存配置需求无法被满足时,调用一个你指定的函数。也就是说,一旦::operator new无法完成任务,在丢出std::bad_alloc异常状态时,会先调用用户所指定的例程。

       2.1 第二级配置器 __default_alloc_template剖析

          SGI第二级配置器的做法是,如果区块足够大,超过128bytes时,就移交第一级配置器处理。当区块小于128bytes时,则以内存池(memory pool)管理:每次配置一大块内存,并维护对应之自由链表(free-list)。下次若再有相同大小的内存需求,就直接从free-list中取出。如果客户端释还小额区块,就由配置器回收到free-list中。为了方便管理,SGI第二级配置器会主动将任何小额区块的内存需求量上调至8的倍数,并维护16个free-list,各自管理大小分别为8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128bytes的区块。

    //free-lists的节点结构如下:
      union obj{
           union obj* free_list_link;
           char client_data[1]; //The client sees this
      };

      第二级配置器的部分源码:

    //free-lists的节点结构如下:
      union obj{
           union obj* free_list_link;
           char client_data[1]; //The client sees this
      };
      
      enum {__ALIGN=8};//小型区块的上调边界
      enum {__MAX_BYTES=128}; //小型区块的上限
      enum {__NFREELISTS=__MAX_BYTES/__ALIGN}; //free-lists的个数
      
      //以下是第二级配置器
      template <bool threads,int inst>
      class __default_alloc_template{
      private:
            //ROUND_UP()将bytes上调至8的倍数
            static size_t ROUND_UP(size_t bytes){
                 return ((bytes)+__ALIGN-1) & ~(__ALIGN-1));
            }
            
      private:
            union obj{  //free-lists的节点构造
                 union obj* free_list_link;
                 char client_data[1];
            };
            
      private:
           //16个free-lists
           static obj* volatile free_list[__NFREELISTS];
           //以下函数根据区块大小,决定使用第n号free-lists.n从1开始
           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);
           
           //chunk allocation state
           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 old_sz,size_t new_sz);
      };
      
      //以下是static data memeber的定义与处置设定
      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;
          
      

     

  • 相关阅读:
    Vuforia7+Unity2017.3.f3实践练习
    pureMVC+unity
    sql语法小结
    ScreenToViewportPoint,WorldToViewportPoint,ViewportToWorldPoint的运用,实现一个简单的对三维中物体的拖拽移动效果
    Unity游戏开发学习之路——数据持久化
    Unity游戏开发之路上的那些坑——NullReferenceException
    ffmpeg-音频视频处理
    微信开发之(五)接收语音识别结果
    微信开发之(五)获取media_id的值
    微信开发之(五)微信获取自定义菜单
  • 原文地址:https://www.cnblogs.com/sixue/p/4316708.html
Copyright © 2011-2022 走看看