STL空间配置器

来源:互联网 发布:网络信贷 编辑:程序博客网 时间:2024/06/06 09:17

为什么要有空间配置器?
1.内存碎片问题(外碎片)

    由于频繁分配、释放小块内存容易在堆中造成外碎片(极端情况下就是堆中空闲的内存总量满足一个请求,但是这些空闲的块都不连续,导致任何一个单独的空闲的块都无法满足这个请求)。

2.频繁的分配小块内存,效率比较低。

   开辟空间的时候,分配器去找一块空闲块给用户。找空闲块也是需要时间的,尤其是在外碎片比较多的情况下。   如果分配器其找不到,就要考虑处理假碎片现象(释放的小块空间没有合并),这时候就要将这些已经释放的的空闲块进行合并,这也是需要时间的。   malloc在开辟空间的时候,这些空间会带有一些附加的信息,这样的话也就造成了空间的利用率有所降低,尤其是在频繁申请小块内存的时候。

这里写图片描述
STL中的空间配置器分为两种:
一级空间配置器
二级空间配置器
在STL中默认如果要分配的内存大于128个字节的话就是大块内存,调用一级空间配置器直接向系统申请。
如果小于等于128个字节的话则认为是小内存,则就去内存池中申请。一级空间配置器很简单,直接封装了malloc和free处理,增加_malloc_alloc_oom_handle处理机制。二级空间配置器才是STL的精华,二级空间配置器主要由内存池和自由链表构成。

框架设计

这里写图片描述

这里写图片描述


流程及代码实现

这里写图片描述
“内存不足处理机制”是客户端的责任,设置”内存不足处理介质”也是客户端的责任。

//一级空间配置器template <int inst>class __MallocAllocTemplate{public:    //static void(*__malloc_alloc_oom_handler)();    static HANDLE_FUNC __malloc_alloc_oom_handler;   //HANDLE_FUNC 代表一个函数指针    static void* OOM_Malloc(size_t n)    {        while (1)        {            if (__malloc_alloc_oom_handler == 0)                throw bad_alloc();            __malloc_alloc_oom_handler(); // 释放内存;如果设置了句柄,那么就会一直循环,直到申请到了,才会退出            void* ret = malloc(n);            if (ret)                return ret;        }    }    static void* Allocate(size_t n)    {        void* ret = malloc(n);        if (ret == NULL)   //一级空间配置器没有申请到内存        {            ret = OOM_Malloc(n);        }        return ret;    }    static void Deallocate(void* p, size_t n)    {        free(p);    }    static HANDLE_FUNC SetMallocHandler(HANDLE_FUNC f)    {        HANDLE_FUNC old = f;        __malloc_alloc_oom_handler = f;        return old;    }};

这里写图片描述

//二级空间配置器template <bool threads, int inst>class __DefaultAlloceTemplate{public:    static size_t FREELIST_INDEX(size_t bytes)    {        return ((bytes + __ALIGN - 1) / __ALIGN - 1); //找到链接的index    }    static size_t  ROUND_UP(size_t bytes)    {        return ((bytes)+__ALIGN - 1)&(~(__ALIGN - 1));//变成8的倍数    }    //在内存池中申请对象    static char* Chunk(size_t n, size_t &nobjs)    {        size_t needBytes = n * nobjs;   //需要的大小(即20块内存)        size_t leftBytes = _endfree - _startfree;  //内存池中剩余的大小        //内存池中的内存足够申请n*nobjs大小的内存        if (needBytes <= leftBytes)         //内存池中剩余的内存足够申请20块        {            char* ret = _startfree;            _startfree += needBytes;            return ret;        }        //虽然不够申请needBytes,但是足够申请n大小的内存        else if (leftBytes > n)        {            nobjs = leftBytes / n;     //计算内存池中剩余的内存足够申请多少块            needBytes = n*nobjs;                    char* ret = _startfree;            _startfree += needBytes;            return ret;        }        //剩余的内存连1个都申请不到        else        {            if (leftBytes > 0)      //虽然不够申请一个,但是内存池中还有内存            {                size_t index = FREELIST_INDEX(leftBytes);       //将剩下的内存挂入自由链表                ((Obj*)_startfree)->free_list_link = _freeList[index];                (_freeList[index])->free_list_link = (Obj*)_startfree;            }            size_t byteToGets = needBytes * 2 + ROUND_UP(_heapsize >> 4);       //malloc内存            _startfree = (char*)malloc(byteToGets);            if (_startfree == NULL)            //malloc失败了            {                size_t index = FREELIST_INDEX(n);       //去index后面的自由链表中找比它大的内存块,找到则放入内存池                for (size_t i = index; i < __NFREELISTS; ++i)                {                    if (_freeList[i])                    {                        _startfree = (char*)_freeList[i];                        _freeList[i] = ((Obj*)_startfree)->free_list_link;                        _endfree = _startfree + (i + 1)*__ALIGN;                        return Chunk(n, nobjs);                    }                }                _endfree = 0;            //自由链表中也没有更大的内存块了,调用一级空间配置器                _startfree = (char*)__MallocAllocTemplate<0>::Allocate(byteToGets);            }            _heapsize = _heapsize + byteToGets;            _endfree = _startfree + _heapsize;            return Chunk(n, nobjs);        }    }    //填充自由链表    static char* Refill(size_t n)    {        size_t nobjs = 20;      //一次申请二十个对象        char* ret = Chunk(n, nobjs);             if (nobjs == 1)       //nobjs=1说明只从内存池中拿到了一块内存,直接返回        {            return ret;        }        else        {            size_t index = FREELIST_INDEX(n);            _freeList[index] = (Obj*)(ret + n);   //把剩下的内存块挂入自由链表            Obj* cur = _freeList[index];            for (size_t i = 1; i < nobjs; ++i)            {                Obj* next = (Obj*)((char*)cur + n);                cur->free_list_link = next;                cur = next;            }            cur->free_list_link = NULL;            return ret;        }    }    static void* Allocate(size_t n)    {        if (n > __MAX_BYTES)        {            return __MallocAllocTemplate<0>::Allocate(n); //如果申请的内存块大于128,则直接调用一级空间配置器的Allocate        }        size_t index = FREELIST_INDEX(n);   //计算出index        if (_freeList[index])   //index下面挂的有所需内存块        {            Obj* ret = _freeList[index];                _freeList[index] = ret->free_list_link;            return ret;        }        else            return Refill(ROUND_UP(n));     //index下面没有所需内存块,则开始填充自由链表    }    static void Deallocate(void* ptr, size_t n)    {        if (n > __MAX_BYTES)        {            __MallocAllocTemplate<0>::Deallocate(n);        }        size_t index = FREELIST_INDEX(n);  //将还回来的内存挂入自由链表        Obj* obj = (Obj*)ptr;        obj->_freeListLink = _freeList[index];        _freeList[index] = obj;    }private:    enum { __ALIGN = 8 };    enum { __MAX_BYTES = 128 };    enum { __NFREELISTS = __MAX_BYTES / __ALIGN };    union Obj {        union Obj * free_list_link;        char client_data[1];    /* The client sees this. */    };//管理自由链表    //狭义内存池    static Obj*_freeList[__NFREELISTS];    static char*_startfree;    static char*_endfree;    static size_t _heapsize;};template <bool threads, int inst>char* __DefaultAlloceTemplate<threads, inst>::_startfree = 0;template <bool threads, int inst>char* __DefaultAlloceTemplate<threads, inst>::_endfree = 0;template <bool threads, int inst>size_t __DefaultAlloceTemplate<threads, inst>::_heapsize = 0;template <bool threads, int inst>typename __DefaultAlloceTemplate<threads, inst>::Obj*__DefaultAlloceTemplate<threads, inst>::_freeList[__NFREELISTS] = { 0 };