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,第一个版本,接收一个对象指针,调用其析构函数

//1template<class T>
inline void destroy(T * pointer){
    pointer->~T();
}

  2,第二个版本,接受一个迭代器区间

//2template<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了,就再次进行分配,至少能分到一个区块了
    }

}
时间: 2024-10-11 20:11:58

stl 空间配置器理解的相关文章

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

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

STL空间配置器那点事

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

STL空间配置器

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

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空间配置器、vector、list、deque、map复习

本文写于2017-03-03,从老账号迁移到本账号,原文地址:https://www.cnblogs.com/huangweiyang/p/6440830.html STL的六大组件:容器.算法.迭代器.空间配置器.容器适配器.仿函数. 空间配置器 空间配置器产生的缘由:由于程序需求,很多小块内存在程序中动态申请.释放.于是就容易出现内存外部碎片问题,同时由于一直调用malloc系统调用,产生性能问题. (注:内碎片:因为内存对齐/访问效率而差生如用户需要3字节,实际得到4字节的问题,其中的碎片

stl空间配置器alloc

SGI设计了双层级配置器,第一级配置器直接使用malloc()和free(),第二级配置器视情况采用不同的策略:当配置区块超过128bytes时,调用第一级配置器,当配置区块小于128bytes时,采用内存池方式 //SGI第一级配置器 template<int inst> class __malloc_alloc_template { private: //处理内存不足的情况 //c++ new handler机制:系统在内存配置需求无法被满足时,调用一个指定函数,参见effective c

STL——空间配置器

__malloc_alloc_template分配器:该分配器是对malloc.realloc以及free的封装: 当调用malloc和realloc申请不到内存空间的时候,会改调用oom_malloc()和oom_realloc(),这两个函数会反复调用用户传递过来的out of memory handler处理函数,直到能用malloc或者realloc申请到内存为止. 如果用户没有传递__malloc_alloc_oom_handler,__malloc_alloc_template会抛出

STL 源码剖析读书笔记一:空间配置器

1. STL 的空间配置器 STL 空间配置器在运用的角度来说,是最不需要介绍的,它总是隐藏在一切组件背后.但若以 STL 的实现角度而言,第一个需要理解的就是空间配置器. 根据 STL 规范,以下是 allocator 的必要接口: allocator::value_type allocator::pointer allocator::const_pointer allocator::reference allocator::const_reference allocator::size_ty