CPP_STL——内存池(附源代码)

内存碎片
讲述内存池之前,先来说一下内存碎片的概念。物理内存实际的存储空间是连续的,内存没有被使用之前,进程刚跑起来的时候确实是顺序使用的,但是在系统中不断有进程提起,退出。这样内存在不停的被申请和释放,可用的物理内存空间不再连续,会产生很多的碎片,这就是内存碎片。
内存碎片过多,将造成内存的可用性降低,实际能用的内存没有剩余那么多,好比把一个亿分别存到一亿张银行卡一样,刷卡的时候每次只能刷一块钱的商品,有买汽车的能力却买不了,因为没有集中起来。
001
内存池
内存池其实就是进程先向系统申请一大块内存,然后该进程把这块内存管理起来,用的时候直接从这大块内存取,不再向系统索取,直到这大块用完才和系统申请,这样能减少操作系统的负担,提高效率。
并且,对这大块内存进行有效的管理,进程需要使用小内存时,就分配小内存块,需要连续的大内存才分配大内存块。这样有效的减少的内存碎片的产生。

一级空间配置器
其实这个很简单,就是申请的时候还是用malloc跟系统申请内存,释放的时候就释放,只不过,如果申请失败会调用一个能释放内存成功的函数,再申请内存。这个函数在一级空间配置器里用函数指针保存,通常这个指针都是为0,需要自己定义,没有定义又申请内存失败就会抛异常。

二级空间配置器
可以使用空间配置器,对内存池进行有效的管理。每次通过空间配置器向操作系统申请和释放内存,但是实际什么时候申请和释放由配置器决定,图解如下:
002

但是这样还无法解决内存碎片的问题,那么STL里面的空间配置器是如何解决的呢?
1.还是需要使用内存池
2.使用哈希桶,以8字节大小为小内存块,128字节为大内存块,哈希表的长度就是128/8 = 16,每个元素挂着对应内存块的大小为(下标+1)*8
3.进程申请内存,向上取8的整数倍,就直接从哈系表下面取相应大小的内存块,申请大内存用大内存块,申请小内存用小内存块,有效解决内存碎片问题。
4.假如申请15字节内存,去哈希表下标为1的元素下边拿内存块,如果已经拿完了,就去内存池取,每次去内存池取的时候都取20块16字节内存,然后挂19块,返回1块给进程。不足20块,就有多少块拿多少块。
5.如果申请的内存大于最大内存块,那就和一级空间配置器申请吧
6.如果内存池的内存也用完啦咋办呢?那就只能向操作系统再要一大块内存给内存池补点货了。但是也是存在申请失败的情况,失败了在哈希表里一直往右搜索比申请的内存更大的内存块,找到了就给了,找到最后还是没有,就去和一级空间配置器商量一下要点内存吧。
7.释放内存,其实只是把释放的内存块挂在了哈希表下面,只有超过了最大内存块的内存才会调用一级空间配置器释放。
003

代码

#pragma once

#include "TraceLog.h"

typedef void(*HANDLE_FUNC)();

//一级空间配置器
template <int inst>
class __MallocAllocTemplate {

private:
    static void* OOM_Malloc(size_t n)   //释放空间再申请空间
    {
        while (1)
        {
            if (__malloc_alloc_oom_handler == 0)
            {
                throw bad_alloc();
            }
            __malloc_alloc_oom_handler();

            void *p = malloc(n);
            if (p)
                return p;
        }
    }


    static HANDLE_FUNC __malloc_alloc_oom_handler;
public:
    static void* Allocate(size_t n)
    {
        void *p = malloc(n);
        if (p == NULL)
            p = OOM_Malloc(n);

        return p;
    }

    static void Deallocate(void *p, size_t /* n */)
    {
        free(p);
    }

    static HANDLE_FUNC SetMallocHandler(HANDLE_FUNC f)  //给释放内存函数指针赋值
    {
        HANDLE_FUNC old = __malloc_alloc_oom_handler;
        __malloc_alloc_oom_handler = f;
        return old;
    }
};

template<int inst>
HANDLE_FUNC __MallocAllocTemplate<inst>::__malloc_alloc_oom_handler = 0;        //类外初始化

#include <windows.h>

void FreeHandle()
{
    cout<<"释放内存"<<endl;
    Sleep(1000);
}

void TestMallocAlloc()
{
    __MallocAllocTemplate<0>::SetMallocHandler(FreeHandle);
    void* p1 = __MallocAllocTemplate<0>::Allocate(10);
    void* p2 = __MallocAllocTemplate<0>::Allocate(0x7fffffff);
}


/////////////////////////////////////////////////////////
//

//二级空间配置器(内存池)
template <bool threads, int inst>
class __DefaultAllocTemplate
{
public:
    static  size_t FREELIST_INDEX(size_t bytes)      //查找索引
    {           
        return (((bytes) + __ALIGN-1)/__ALIGN - 1);
    }

    static size_t ROUND_UP(size_t bytes) {                  //向上取8的倍数
        return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));
    }

    static char* ChunkAlloc(size_t size, size_t& nobjs)         //大块申请
    {
        size_t totalSize = size * nobjs;
        size_t realSize = _endfree - _startfree;
        if (realSize >= totalSize)
        {
//          __TRACE_DEBUG("内存池中有足够20个对象大块内存\n");
            char* chunk = _startfree;
            _startfree += totalSize;
            return chunk;
        }
        else if (realSize >= size)
        {
//          __TRACE_DEBUG("内存池中只有%u个对象大块内存\n", nobjs);
            nobjs = realSize / size;
            char* chunk = _startfree;
            _startfree += size*nobjs;
            return chunk;
        }
        else
        {
//          __TRACE_DEBUG("内存池中一个对象都不够\n");
            if (realSize > 0)
            {
                size_t index = FREELIST_INDEX(realSize);
                ((Obj*)_startfree)->_freelistlink = _freelist[index];
                _freelist[index] = (Obj*)_startfree;
            }

//          __TRACE_DEBUG("向系统申请%ubytes字节\n", byetstoget);
            //内存池没空间了,和系统申请
            realSize = totalSize * 2 + ROUND_UP(_heapsize >> 4);
            _startfree = (char*)malloc(realSize);
            if (_startfree == NULL)     //申请失败
            {
                //取出大于size的链表下的内存块使用
                size_t index = FREELIST_INDEX(totalSize);
                while (index < __NFREELISTS)  //小于哈希表的长度
                {
                    if (_freelist[index] != NULL)       //找到不为空的结点下标
                    {
                        _startfree = (char*)_freelist[index];           //取出来
                        _freelist[index] = _freelist[index]->_freelistlink;  

                        _endfree = _startfree + (index + 1)*__ALIGN;
                        return ChunkAlloc(size, nobjs);
                    }
                    index++;
                }

                //走到这里说明,哈希表index+后面也没有挂多余的内存了
                //和系统又申请失败, 则用一级空间配置器申请
                _startfree = (char*)__MallocAllocTemplate<0>::Allocate(realSize);
            }

            _endfree = _startfree + realSize;
            _heapsize += realSize;
            return ChunkAlloc(size, nobjs);
        }
    }

    static void* Refill(size_t size)        //填充
    {
        size_t nobjs = 20;

        char* p = ChunkAlloc(size, nobjs);
        if (nobjs == 1)
            return p;
        size_t index = FREELIST_INDEX(size);
        char* cur = p;
        p += size;
        _freelist[index] = (Obj*)p;
        for (size_t i = 0; i < nobjs-2; i++)
        {
            Obj* next = (Obj*)(p+size);
            ((Obj*)p)->_freelistlink = next;
            p = (char*)next;
        }
        ((Obj*)p)->_freelistlink = NULL;
        return cur;
    }

    static void* Allocate(size_t n)         //申请内存
    {
        if (n > __MAX_BYTES)        //申请大小超过最大内存大小,就用一级空间配置器申请空间返回
        {
            return __MallocAllocTemplate<0>::Allocate(n);
        }
        else                      //申请大小小于最大内存块
        {
            size_t index = FREELIST_INDEX(n);
            if (_freelist[index] == NULL)
            {
                return Refill(ROUND_UP(n));     //没有挂空间就去申请挂一些
            }
            else
            {
                Obj* cur = _freelist[index];
                _freelist[index] = _freelist[index]->_freelistlink;
                return cur;
            }
        }

    }

    static void Deallocate(void* p, size_t n)
    {
        //判断n是否大于最大内存块
        if (n > __MAX_BYTES)
        {
            __MallocAllocTemplate<0>::Deallocate(p, n);
        }
        else   //把释放的空间挂回哈希表,不直接还给系统
        {
            size_t index = FREELIST_INDEX(n);
            ((Obj*)p)->_freelistlink = _freelist[index];
            _freelist[index] = (Obj*)p;
        }
    }


protected:
    enum {__ALIGN = 8};         //小块内存
    enum {__MAX_BYTES = 128};   //大块内存
    enum {__NFREELISTS = __MAX_BYTES/__ALIGN};      //哈希表长度 128/8=16

    union Obj
    {
        union Obj* _freelistlink;   //char** 二级指针
        char _client[1];
    };

    // 自由链表配合管理
    static Obj* _freelist[__NFREELISTS];

    // 内存池
    static char* _startfree;        //开始
    static char* _endfree;          //结束
    static size_t _heapsize;        //内存池大小
}; 

template <bool threads, int inst>
typename __DefaultAllocTemplate<threads, inst>::Obj*
__DefaultAllocTemplate<threads, inst>::_freelist[__NFREELISTS] = {0};

// 内存池
template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_startfree = NULL;

template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_endfree = NULL;

template <bool threads, int inst>
size_t __DefaultAllocTemplate<threads, inst>::_heapsize = 0;

#ifdef __USE_MALLOC
typedef __MallocAllocTemplate<0> alloc;
#else
typedef __DefaultAllocTemplate<false, 0> alloc;
#endif // __USE_MALLOC


void TestDefaultAlloc()
{
    for(size_t i = 0; i < 21; ++i)
    {
        __TRACE_DEBUG("申请第%u个对象\n", i);
        __DefaultAllocTemplate<false, 0>::Allocate(7);
    }
}

template<class T, class Alloc>
class SimpleAlloc {

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)); }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值