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;

时间: 2024-10-29 19:08:31

STL-空间配置器(allocator)的相关文章

C++ STL学习之 空间配置器(allocator)

标签(空格分隔): C++ STL 众所周知,一般情况下,一个程序包括数据结构和相应的算法,而数据结构作为存储数据的组织形式,与内存空间有着密切的联系. 在C++ STL中,空间配置器便是用来实现内存空间(一般是内存,也可以是硬盘等空间)分配的工具,他与容器联系紧密,每一种容器的空间分配都是通过空间分配器alloctor实现的.理解alloctor的实现原理,对内存结构以及数据存储形式会有更清晰直观的认识. 1.两种C++类对象实例化方式的异同 在c++中,创建类对象一般分为两种方式:一种是直接

STL空间配置器那点事

STL简介 STL(Standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等,STL也是算法和其他一些组件的集合. 谈及组件,那么我们就首先来简单谈下STL六大组件,其相关的设计模式使用,以及各组件之间的协作关系. 设计模式一览 六大组件简单介绍 1. 空间配置器:内存池实现小块内存分配,对应到设计模式--单例模式(工具类,提供服务,一个程序只需要一个空间配置器即可),享元模式(小块内存统一由

stl空间配置器线程安全问题补充

摘要 在上一篇博客<STL空间配置器那点事>简单介绍了空间配置器的基本实现 两级空间配置器处理,一级相关细节问题,同时简单描述了STL各组件之间的关系以及设计到的设计模式等. 在最后,又关于STL空间配置的效率以及空间释放时机做了简单的探讨. 线程安全问题概述 为什么会有线程安全问题? 认真学过操作系统的同学应该都知道一个问题. first--进程是系统资源分配和调度的基本单位,是操作系统结构的基础,是一个程序的运行实体,同时也是一个程序执行中线程的容器 seconed--进程中作为资源分配基

STL空间配置器

1.什么是空间配置器? 空间配置器负责空间配置与管理.配置器是一个实现了动态空间配置.空间管理.空间释放的class template.以内存池方式实现小块内存管理分配.关于内存池概念可以点击:内存池. 2.STL空间配置器产生的缘由 在软件开发,程序设计中,我们不免因为程序需求,使用很多的小块内存(基本类型以及小内存的自定义类型).在程序中动态申请,释放.这个过程过程并不是一定能够控制好的,于是乎出现以下问题: 问题1:就出现了内存碎片问题.(ps外碎片问题) 问题2:一直在因为小块内存而进行

STL源码剖析 — 空间配置器(allocator)

前言 以STL的实现角度而言,第一个需要介绍的就是空间配置器,因为整个STL的操作对象都存放在容器之中. 你完全可以实现一个直接向硬件存取空间的allocator. 下面介绍的是SGI STL提供的配置器,配置的对象,是内存.(以下内容来自<STL源码剖析>) 空间配置器的标准接口 根据STL的规范,allocator的必要接口 各种typedef 1 allocator::value_type 2 allocator::pointer 3 allocator::const_pointer 4

STL学习笔记--2、空间配置器 allocator

2.1标准接口 allocator::value_type allocator::pointer allocator::const_pointer allocator::reference allocator::const_reference allocator::size_type allocator::difference_type allocator::rebind allocator::allocator()//默认构造函数 allocator::allocator(const allo

STL源码剖析——空间配置器Allocator#2 一/二级空间配置器

上节学习了内存配置后的对象构造行为和内存释放前的对象析构行为,在这一节来学习内存的配置与释放. C++的内存配置基本操作是::operator new(),而释放基本操作是::operator delete().这两个全局函数相当于C的malloc() 和free() 函数.而SGI正是以malloc() 和free() 完成内存的配置与释放. 考虑到小型区块可能造成的内存破碎问题,SGI设计了两级的空间配置器.第一级直接使用malloc() 和free() ,而第二级则视情况采用不同的策略:当

STL——空间配置器(SGI-STL)

一. 空间配置器标准接口 参见<STL源码剖析>第二章-2.1. 二.具备次配置力的SGI空间配置器 SGI STL的配置器与众不同,也与标准规范不同,其名称是alloc而非allocator,而且不接受任何参数(虽然SGI也定义有一个符合部分标准.名为sllocator的配置器,但SGI自己从未用过它,也不建议使用,主要因为效率不佳).这并不会带来什么困扰:我们通常很少需要自行指定配置器名称,而SGI STL的每一个容器都已经指定其缺省的空间配置器为alloc. // 在程序中要明白采用SG

STL空间配置器(一)

STL空间适配器(一) Author:胡建 Time:2016/4/5 这是STL学习的第一部分,空间适配器,所谓空间适配器,就是用来管理内存的一个器具.对于STL来说,空间适配器是它可以正常工作的基础,也为它可以高效工作提供了动力.对于使用STL来说,它是不和用户直接打交道的,而是隐藏在一切STL组建之后,默默为各种内存申请提供支持的. 对于c++用户来说,new和delete很熟悉,这两个函数可以分别完成内存的申请和释放,和c里面的malloc和free如出一辙,SGI 有一个标准空间适配器

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(