STL空间配置器

为什么要有空间配置器?
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 };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值