STL之空间配置器

    众所周知,STL中的空间配置器分为一级空间配置器和二级配置器。第一级配置器以malloc( ),free(),realloc()等C函数执行实际的内存配置、释放、重配置操作,并实际出类似C++  new-handler的机制。

    C++ new handler机制是,你可以要求系统在内存配置需求无法被满足时,调用一个你所指定的函数。换句话说,一旦::operator new 无法完成任务,在丢出std::bad_alloc异常状态之前,会先调用由客户指定的处理例程。该处理例程通常即被称为new-handler。

    SGI第一级配置器的allocate()和 realloc()都是在调用malloc()和realloc()不成功后,改调用oom_malloc()和oom_realloc()。后两者都有内循环,不断调用“内存不足处理机制”,期望在某次调用之后,获得足够的内存而圆满完成任务。但如果“内存不足处理例程”并未被客户设定,oom_malloc()和oom_realloc()便毫不客气地调用_THROW_BAD_ALLOC,丢出bad_alloc异常信息,或利用exit(1)硬生生中止程序。

    下面给出具体的代码:

#ifndef _STL_ALLOC_H_
#define _STL_ALLOC_H_

#if 1
#include<iostream>
#include<malloc.h>
#include<exception>
#include<stdlib.h>
#include<string.h>
using namespace std;

#define __THROW_BAD_ALLOC cerr<<"Out Of Memery."<<endl;exit(1);
#elif !defined(__THROW_BAD_ALLOC)
#include<iostream.h>
#define __THROW_BAD_ALLOC cerr<<"out of memory" << endl;exit(1);
#endif

//一级空间配置器
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)();
public:
    //空间申请函数
    static void* allocate(size_t n)
    {
        void *result = malloc(n);
        if(result == 0)
        {
            //第一次申请不成功再次申请
            result = oom_malloc(n);
        }
        return result;
    }
    //空间回收函数
    static void deallocate(void* p,size_t n)
    {
        free(p);
    }
    static void* reallocate(void*p,size_t,size_t new_sz)
    {
        void *result = realloc(p,new_sz);
        if(result == 0){
            result = oom_realloc(p,new_sz);
        }
        return result;
    }
    //set_malloc_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>::__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;
        //如果my_malloc_handler为0,说明用户没有设置内存不足时的处理函数
        //抛出异常
        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;
        }
    }
}


下面我们来看二级空间配置器:

    SGI第二级配置器的做法是,如果区域够大,超过128bytes时,就要移交给第一级配置器处理。当区块小于128bytes时,则以内存池管理,此法又称为次层配置:每次配置一大块内存,并维护对应之自由链表(free-list)。下次若再有相同大小的内存需求,就直接从free-list中拨出,如果客端释还小额区块,就由配置器回收到free-list中。为了方便管理,SGI第二级配置器主动将任何小额区块的内存需求量上调8的倍数,并维护16个free-list。

typedef __malloc_alloc_template<0> malloc_alloc;
///////////////////////////////////////////////////////////////////////////////
//二级空间配置器

enum{__ALIGN = 8};    //小型区块的上调边界
enum{__MAX_BYTES = 128};    //小型区块的上界
enum{__NFREELISTS = __MAX_BYTES / __ALIGN};    //free-list个数


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:
    //free-list的节点构造
    union obj
    {
        union obj * free_list_link;
        char client_data[1];
    };
private:
    //16个free-list
    static obj * volatile free_list[__NFREELISTS];
    //以下的函数根据区块的大小,决定使用第n号的free-list,n从0开始算
    static size_t FREELIST_INDEX(size_t bytes)
    {
        return ((bytes + __ALIGN-1) / __ALIGN-1);
    }
    //内存池的起始位置
    static char* start_free;
    //内存池的结束位置
    static char* end_free;
    static size_t heap_size;
private:
    static void* refill(size_t n);
    static char* chunk_alloc(size_t size,int &nobjs);
public:
    //空间配置函数
    static void* allocate(size_t n)
    {
        obj * volatile *my_free_list;
        obj * result;
        //当n > 128 时,转到一级空间配置器
        if(n > __MAX_BYTES)
        {
            return malloc_alloc::allocate(n);
        }
        //根据n的值,索引适合的小标
        my_free_list = free_list + FREELIST_INDEX(n);
        result = *my_free_list;
        //当free-list为空时,准备重新填充free-list
        if(result == 0){
            void *r = refill(ROUND_UP(n));
            return r;
        }
        *my_free_list = result->free_list_link;
        return result;
    }
    static void deallocate(void *p,size_t n)
    {
        obj *q = (obj*)p;
        obj * volatile * my_free_list;
        if(n > __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;
    }
    static void* reallocate(void *p,size_t old_sz,size_t new_sz);
};

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>
typename __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};

template<bool threads,int inst>
void* __default_alloc_template<threads,inst>::refill(size_t n)
{
    int nobjs = 20;
    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;
}

//内存池
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);
        //将内存池中的剩余零头找到适当的位置插入到自由链表中
        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;
        }
        //通过malloc申请比较大的空间
        start_free = (char*)malloc(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;
                    return chunk_alloc(size,nobjs);
                }
            }
            //程序执行到这时,说明已经山穷水尽
            //调用一级空间配置器,这时一般会抛出异常
            end_free = 0;
            start_free = (char*)malloc_alloc::allocate(bytes_to_get);
        }
        heap_size += bytes_to_get;
        end_free = start_free + bytes_to_get;
        return chunk_alloc(size,nobjs);
    }
}

template<bool threads,int inst>
void* __default_alloc_template<threads,inst>::reallocate(void *p,size_t old_sz,size_t new_sz)
{
    void * result;
    size_t copy_sz;
    
    if(old_sz > (size_t) __MAX_BYTES && new_sz > (size_t)__MAX_BYTES){
        return (realloc(p,new_sz));
    }
    if(ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);
    result = allocate(new_sz);
    copy_sz = new_sz > old_sz ? old_sz : new_sz;
    memcpy(result,p,copy_sz);
    deallocate(p,old_sz);
    return (result);
}
/////////////////////////////////////////////////////////////////////////////////////
//
#ifdef _USE_MALLOC
typedef __malloc_alloc_template<0>malloc_alloc;
typedef malloc_alloc alloc;
#else
typedef __default_alloc_template<0,0>alloc;
#endif


////////////////////////////////////////////////////////////////////////////////////
//
//二次封装
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));
    }
};






#endif




下面给出一个大概的示意图:

STL之空间配置器_第1张图片

你可能感兴趣的:(STL之空间配置器)