• C++ 内存管理学习笔记


    C++ 内存管理

    C++ primitives

    包含new,new[],new(),::operator new(),...
    ::operator new() 本质就是调用malloc
    ::operator delete 本质就是调用free
    举个有意思的例子

    #ifdef __MSC_VER
    int * p4 = allocator<int>().allocate(5, (int*)0);
    allocator<int>().deallocate(p4, 5);
    #endif
    #ifdef __BORLANDC__
    int * p4 = allocator<int>().allocate(5);
    allocator<int>().deallocate(p4, 5);
    //显然这是通过分配器来分配内存
    #endif
    #ifdef __GNUC__
    void* p4 = alloc::allocate(512);
    alloc::deallocate(p4, 512);
    #endif
    //在后续的版本只能够CNUC也更新到类似与BORLANDC
    void *p5 = __gnu_cxx::__pool_alloc<int>().allocate(9);
    __gnu_cxx::__pool_alloc<int>().deallocate((int*)p5, 9);
    //__pool_alloc内存池水
    

    接着举个使用new 的例子
    或者说是new operator的过程

    Complex *pc;
    Complex * pc = new Complex(1, 2);
    ......
    delete pc;
    //下述就是Complex的new operator
    try{
        void * men = operator new(sizeof(Complex));
        pc = static_cast<Complex*>(men);
        pc->Complex::Complex(1, 2);
        //能不能这样写不一定
    }catch(std::bad_alloc){
    
    }
    
    void *operator new(size_t size, const std::nothrowt&) _THROW0*()
    {
        //std::nothrow设置为函数不抛出异常
        void * p;
        while((p = malloc(size)) == 0){
            _TRY_BEGIN
            if(_callnewh(size) == 0) break;
            //callnewh调用这个函数,这玩意可以被自己设定
            _CATCH(std::bad_alloc) return(0);
            _CATCH_END
        }
        return (p);
    }
    //delete的使用过程
    pc->~Complex();
    operator delete(pc);
    
    void __cdecl operator delete(void *p)_THROW0(){
        free(p);
    }
    

    array new, array delete

    Complex* pca = new Complex[3];
    delete[] pca;
    

    malloc设计申请空间会给出一个cookie
    内存结构可以看侯捷的课
    placement new的使用方法大概如下

    new(point)constructor();
    

    使用array new与new的内存空间布局不一样

    placement new

    或指new(p)
    或::operator new(size_t, void *)
    形式如下

    #include <new>
    char * buf = new char[size(Complex) * 3];
    Complex * pc = new(buf)Complex(1, 2);
    delete [] buf;
    
    //经过编译器的处理如下
    
    try{
        void * mem = operator new(sizeof(Complex), buf);
        pc = static_cast<Complex*>(men);
        pc -> Complex::Complex(1, 2);
    }catch(std::bad_alloc){
    
    }
    

    C++应用程序,分配内存的途径

    member function 可重载

    //假设存在一个Foo类
    Foo::operator new(size_t);
    Foo::operator delete(void *);
    

    重载::operator new / :: operator delete
    overloaded function
    例子如下:

    void myAlloc(size_t size){
        return malloc(size);
    }
    void myFree(void * ptr){
        return free(ptr);
    }
    inline void * operator new(size_t size){
        return myAlloc(size);
    }
    inline void * delete delete(void * ptr){
        return myFree(ptr);
    }
    class Foo{
    public:
        static void * operator new(size_t);
        static void * operator delete(void *, size_t);
    }
    void * operator new(size_t size, void * start){
        return start;
    }//这个就是标准库已提供的placement new()的重载形式
    void * operator new(size_t size, long extra){
        return malloc(size + extra);
    }//这个是崭新的placement new
    void * operator new(size_t size, long extra, char init)
    {
        return malloc(size + extra);
    }//这个也是一个崭新的placement new
    

    一次malloc会多8个字节的cookie
    内存池目标:

    1. 速度
    2. 空间
      写一个分配器
    class Screen{
    public:
        static void * operator new(size_t);
        static void operator delete(void*, size_t);
    private:
        Screen* next;
        static Screen* freeStore;
        static const int screenChunk;
    private:
        int i;
    }
    Screen* Screen::freeStore = null;
    const int Screen::screenChunk = 24;
    void * Screen::operator new(size_t size){
        Screen *p;
        if(!freeStore){
            size_t chunk = screenChunk * size;
            freeStrore = p = reinter_cast<Screen*>
            (new char[chunk]);
            for(; p != &freeStore[sceenChunk - 1]; ++ p){
                p -> next = p + 1;
            }
            p -> next = null;
        }
        p = freeStore;
        freeStore = freeStore -> next;
        return p;
    }
    void Screen::operator delete(void *p, size_t){
        (static_cast<Sreen*>(p))->next = freeStore;
        freeStore = static_cast<Screen*>(p);
        //头插法回收内存
        //其实我在这里有个疑问
        //我的问题是,如果空间不够用了呢
        //好吧,其实我是傻逼,不够了,他会对应扩容
        //然后回收的时候连接起来,这样的话,规模会越来越大
    }
    

    上述实现会多一个指针,有第二种实现
    具体看侯捷课程,懒得抄
    (xswl,借出来的内存不还233333)

    static allocator

    当你受困必须为不容的classes重写一遍几乎想通的member operatornew 跟 operator delete,应该有对应的一个类来实现
    就是下述的例子,都交给static allocator来实现

    class Foo{
    public:
     long L;
     string str;
     static allocator myAlloc;
    public:
    Foo(long l):L(l){}
    static void * operator new(size_t size){
        return myAlloc.allocate(size);
    }
    static void operator delete(void * pdead, size_t size)
    {
        return myAlloc.deallocate(pdead, size);
    }
    }
    allocate Foo::myAlloc;
    

    偷懒真是一件神奇的事情,那么根据上述的例子,我们可以选择偷懒一波,所以引出了对应的macro for static allocator
    例子如下

    #define DECLARE_POOL_ALLOC() 
    static void * operator new(size_t size){
        return myAlloc.allocate(size);
    }
    static void operator delete(void * pdead, size_t size)
    {
        return myAlloc.deallocate(pdead, size);
    }
    #define IMPLEMENT_POOL_ALLOC(class_name)
    allocator class_name::myAlloc;
    
    
    class Foo{
        DECLARE_POOL_ALLOC()
    };
    IMPLEMENT_POOL_ALLOC(FOO)
    //我猜等等应该要讲template
    

    内容补充:
    new handler
    抛出exception之前会先调用一个可由client指定的handler
    以下是new handler的形式与设定方法

    typedef void(*new_handler)();
    new_handler set_new_handler(new_handler p)throw();
    

    new handler只有两个选择

    1. 让更多内存可用
    2. 调用abort()或exit()
      举个使用例子
    void noMoreMemory(){
        cerr << "out of memory";
        abort();
    }
    set_new_handler(noMoreMemory);
    

    std::allocator

    海量小区块 会导致cookie的浪费
    VC6标准allocator只是以::operator new 和 ::operator delete
    完成allocate()和deallocate(),没有任何特殊的设计
    BC5与VC6 allocator一样的实现效果

    G2.9容器使用的分配器,不是std::allocator而是std::alloc
    G4.9变化为__pool_alloc(在__gnu_cxx的命名空间)
    G4.9有许多extended_allocators

    G2.9 std::alloc运行模式
    分配器提供的两个重要函数 allocated eallocate
    free_list[16] 超过分配器所能分配的大小
    就调用malloc来分配
    #0 #1 #2 #3 #4 #5 #6 ... #9 #10 #11 #12 #13 #14 # 15
    8byte 32byte
    每个相差8字节
    alloc 通过一次要20个大小的,然后还会多申请20个
    但是后面的20个是备用的
    memory pool战备池大小 20个
    cookie free blocks用来节约cookie的损耗
    embedded pointers:嵌入式指针,通过暂时占用4个字节的内存
    设计如下

    struct obj{
        union obj* free_list_link;
    }
    

    对象本身要大于等于4字节才能被借用

    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, 0,};
    //---------------------------
    typedef _default_alloc_template<false, 0> alloc;
    
    假设申请32bytes
    由于pool为空,所以请求并成功向pool中注入32*20*2+RoundUP(0>>4) = 1280,再从中切出1个区块返回客户(容器)
    19个剩余的区块返回给list #3
    这个时候累计的申请量: 1280
    pool大小: 640
    
    假设接着申请64bytes
    发现对应的#7是空的,从pool切割出来需要的区块
    连接到#7上,数量永远在1~20之间
    申请64bytes,由于pool有余,所以取pool分割为
    640 / 64 = 10,给一个给容器
    9个放在#7上
    pool大小:0
    
    接着申请96bytes,因为pool == 0
    所以malloc取得96*20*2 + ROUNDUP(1280>>4)
    其中19个区块给list#11,1个给容器,留下来2000备用
    累积申请量:5200
    pool大小留下来:2000
    
    申请88bytes
    因为pool有剩,取20个分给#10,取出一个给容器
    pool:2000 - 88 * 20 = 240
    累积申请量:5200
    pool大小:240
    
    申请8bytes
    分割出20个区块给#0,取出一个给容器
    19个挂在#0
    累积申请量:5200
    pool大小:80
    
    申请104,list#12 无区块
    pool不足供应一个
    先将pool 80 给list#9
    pool->0
    然后索取并取得104*20*2 + RoundUp(5200 >> 4)
    切出19个放到list #12
    1个给容器
    累积申请量为:9688bytes
    pool大小:2408
    
    申请112,由于pool有剩余,从pool取20个区块
    1个返回容器,19个放到#13上
    累积申请量:9688
    pool大小:2408 - 112 * 20 = 168
    
    申请48,由于pool有剩余量,从pool取3个区块
    1个给客户,2个连接到#5上
    累积申请量:9688
    pool大小:168 - 48 * 3 = 24
    
    如果将system heap设置为10000,目前已经取出了9688
    申请72bytes,因为碎片为24,将其分配给list#2,然后要求申请72 * 20 * 2 + RoundUp(9688 >> 4),
    因此无法满足此次要求,于是alloc从手中资源最接近的80(list#9)回填给pool,然后切出72给客户,留下来8
    累积申请大小:9688
    pool大小:8
    
    再要一次72bytes,那么list#8无可用区间,pool剩余不足
    然后申请72 * 20 * 2 + RoundUp(9688 >> 4)
    失败
    所以从资源最接近者拿走一个区块88(liat#10)回填pool
    然后申请一个72给出去
    累积申请量:9688
    pool剩余大小:16
    
    申请120bytes
    list#14无可用区块,申请为120 * 20 * 2 + RoundUp(9688 >> 4)
    但是空间不足,然后也没有右边的空间可用使用
    然后申请失败
    

    G2.9 std::alloc源码剖析

    以下为第一级分配器

    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 == null) result = oom_malloc(n);
            return result;
        }
        static void deallocate(void *p, size_t /* n */){
            free(p);
        }
        static void * reallocate(void * p, size_t/* old_sz */, size_t new_sz){
            void * result = realloc(p, new_sz);
            if(result == null){
                result = oom_realloc(p, new_sz);
            }
            return result; 
        }
        static void (*set_malloc_handler(void (*f)()))(){
            void (*old)() = __malloc_alloc_oom_handler;
            __malloc_alloc_oom_handler = fl
            return (old);
        }
    }
    

    emmmm, 然后到g4.9没有第一级分配器

    第二级分配器

    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){
            return ((bytes) + __ALIGN - 1) & ~(__ALIGN - 1));
        }
    private:
        union obj{
            union obj* free_list_link;
        };//显然这是嵌入式指针
    private:
        static obj* volatile free_list{__NFREELISTS};
        static size_t FREELIST_INDEX(size_t bytes){
            return (((bytes) + __ALIGN - 1) / __ALIHN - 1));
        }//显然这个function是用来处理这是第几个块的
        static void *refill(size_t n);
        static char* chunk_alloc(size_t size, int &nobjs);
        static char* start_free;//指向'pool'的头
        static char* end_free;//指向‘pool’的尾
        static size_t heap_size;
    public:
        
        static void * allocate(size_t n){
            obj * volation *my_free_list;
            obj * result;
            if(n > (size_t)__MAXN_BYTES){
                //如果大于15号这个块 即128bytes
                return (malloc_alloc::allocate(n));
            }
            my_free_list = free_list + FREELIST_INDEX(n);
            //上述判断在哪个对应的#上
            result = *my_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 > (size_t)__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;
            /*
            其实这个上述的回收很简单
            先把q->next_list_link,把回收块的next指向当前
            二级指针指向的块,然后把二级指针指向回收块
            头插法回收
            */
        }
        static void * reallocate(void * p, size_t old_sz,size_t new_sz);
    }
    template <bool threads, int inst>
    void * __default_alloc_template<threads, inst>::
    resfill(size_t n){
        int nobjs = 20;
        char * chunk = chunk_alloc(n, nobjs);
        //nobjs是pass by reference
        obj * volatile * my_free_list;
        obj * result;
        obj * current_obj;
        obj * next_obj;
        int i;
        if (nobjs == 1) 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){
            currnet_obj = next_obj;
            next_obj = (obj*)((char*)next_obj + n);
            //将指针指向的地方转型为obj
            if(nojs - 1 == i){
                current_obj -> free_list_link = null;
                break;
            }else{
                currnet_obj -> free_list_link = next_obj;
            }
        }
        return (result);
    }
    template <bool threads, int inst>
    char *
    __default_alloc_template<threads, intst>::
    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){
            //pool空间可以满足20块的需求
            result = start_free;
            start_free += total_bytes;
            //降低pool水位
            return (result);
        }else if(bytes_left >= size){
            //pool空间只满足大于1块以上需求
            nobjs = bytes_left / size;
            //改变需求量
            total_bytes = size * nobjsl
            result = start_free;
            start_free += total_bytes;
            //降低pool水位
            return (result);
        }else{//pool空间无法满足一块的需求
            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;
            }
            start_free = (char*)malloc(bytes_to_get);
            if(start_free == 0){
                int i;
                obj* volatile *my_free_list, *p;
                for(i = size; i <= __MAX_BYTES;
                i += __ALGIN){
                    my_free_list = free_list + 
                    FREELIST_INDEX(i);
                    p = * my_free_list;
                    if(p != 0){
                        *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;
            //调整pool(调整尾端)
            //其实我刚才疑惑了一下,怎么保证start_free跟
            //end_free是连续的
            //答:因为只有空间不够才会申请,所以每次申请
            //都是一段连续的空间
            return (chunk_alloc(size, nobjs));
        }
    }
    

    obj * volatile * my_free_list, *p;
    这样写导致很多误解
    写成
    obj ** p1;
    obj * p2;

    static void (set_malloc_handler)
    ==>
    typedef void(
    H)();
    static H set_malloc_handler()

    G4.9 使用很棒的分配器
    list<double, __gnu_cxx::__pool_alloc>lst;
    可以通过实验发现,alloc分配的内存大于常规分配器
    当然我的猜想是,可能pool里面有剩余的内存没被使用
    因为每次要的空间都是40倍 + GOUND_UP(X >> 4)

    malloc/free详解

    新版本废除了SBH系列的函数
    malloc里精巧设计SBH

    1. _heap_init
    int __cdecl_heap_init(int mtflag){
        if((_crtheap = HeapCreate(mtflag ? 0:
        HEAP_NO_SERIALIZE,BYTES_PER_PAGE, 0))
        == NULL)    return 0;
        //_PER_PAGE = 4096
        if(__sbh_heap_init() == 0){
            HeapDestory(_crtheap);
            return 0;
        }
        return 1;
        __sbh_pHeaderDefer = null;
    }
    int __cdecl__sbh_heap_init(void){
        if(!(__sbh_pHeaderList = 
        HeapAlloc(_crtheap,0,(16 *
        sizeof(HEADER)))
        ))
        return FALSE;
        __sbh_pHeaderScan = __sbh_pHeaderList;
        __sbh_pHeaderDefer = NULL;
        __sbh_cntHeaderList = 0;
        __sbh_sizeHeaderList = 16;
        return TRUE;
    }
    
    1. _ioinit()
    #ifdef _DEBUG
    #define _malloc_crt malloc
    #else 
    #define _THISFILE __FILE__
    #define _malloc_crt(s) _malloc_dbg(
        s, _CRT_BLOCK,_THISFILE,__LINE__)
    #endif
    void __cdecl_ioinit(void){
        if(pio = _malloc_crt(IOINFO_ARRAY_ELTS
        *sizeof(ioinfo))){
        }
    }//第一次内存分配动作,分配256  32 * 8
    
    1. _heap_alloc_dbg
    #define nNoMansLandSize 4
    typedef struct _CrtMemBlockHeader{
        struct _CrtMemBlockHeader * pBlockHeaderNext;
        struct _CrtMemBlockHeader * pBlockHeaderPrev;
        char *      szFileName;
        int         nLine;
        size_t      nDataSize;
        int         nBlockUse;
        long        IRequest;
        unsigened char gap[nNoMansLandSize];
    }_CrtMemBlockHeader;
    blockSize = sizeof(_CrtMemBlockHeader) + nSize
    + nNoMansLandSize;
    pHead = (_CrtMemBlockHeader*)_heap_alloc_base(
        blockSize
    );
    
    1. _heap_alloc_base()
    if(size <= __sbh_threshold){
        pvReturn = __sbh_alloc_block(size);
        if(pvReturn) return pvReturn;
    }
    if(size == 0) size = 1;
    size = (size + ...) & ~(....);
    return HeapAlloc(_crtheap, 0, size);
    
    1. __sbh_alloc_block()
    sizeEntry = (intSize + 2 * sizeof(int)
        + (BYTES_PER_PARA - 1)
    )
    & ~(BYTES_PER_PARA - 1);
    //起到的功能是调整到16的倍数
    
    1. __sbh_alloc_new_region()
      每一块真正的内存由Group0指向即两个指针指向
      page会被连接到最后一个group上
      结构如下
    typedef struct tagRegion{
        int indGroupUse;//表示在用哪个group
        char cntRefionSize[64];
        BITVEC bitGroupHi[32];
        BITVEC bitGroupLo[32];
        struct tagGroup grpHeadList[32];
    }REGION, *PREGION;
    typedef struct tagGroup{
        int cntEntries;//释放-1 分配+1 如果为0 可以归还给操作系统
        struct tagListHead listHead[64];
    }
    Group, *PGROUP;
    typedef struct tagListHead{
        struct tagEntry* pEntryNext;
        struct tagEntry* pEntryPrev;
    }
    LISTHEAD, *PLISTHEAD;
    typedef struct tagEngtry{
        int sizeFront;
        struct tagEntry * pEntryNext;
        struct tagEntry * pEntryPrev;
    }
    ENTRY, *PENTRY;
    

    由ioinit.c line#81申请100h
    区块大小130h应该由#18lists提供
    通过VirtualAlloc(0, 1MB, MEM_RESERVE, ....)
    通过系统提供的函数来分配对应的内存
    通过HeapAlloc(crtheap,....)
    获取sizeof(REGION)
    通过VirtualAlloc(addr,32kb,MEM_COMMIT)
    真正获取内存
    使用64个bit来标记是否有东西
    又有32行,对用相应的32个group

    free过程
    假设返回一个240h的内存
    240h / 10h -> 36
    由35号链表接收 即group中的第35根指针接受 cookie最后一位变0
    前置的内嵌型指针又恢复了
    指针连接到35号链表 更改bitmap 35位 变成1
    将cntEntries -= 1

    通过遍历pHeaderList的指针来查找内存 看是否在对应的内存区间
    然后在把对应的p / 32k - 1,就可以判断落在哪个group
    通过cookie / 16来发现落在哪个tagListHead
    其实是分段式管理,一段是32Kb
    比较容易判断一段是全回收,便于归还操作系统
    cntEntries为0就是全回收了
    有两个全回收 才归还一次

    defering

    __sbh_pHeaderDefer 是个指针
    指向一个全回收的group所属的header
    当有出现第二个全回收group出现
    sbh才释放这个defer group
    将现在新出现的全回收group设为Defer

    loki::allocator

    Chunk FixedAllocator SmallObjAllocator
    结构大概如下
    SmallObjAllocaroe

    pool_:vetor<FixedAllocator>
    pLastAlloc: FixedAllocator*
    pLastDealloc: FixedAllocator*
    chunkSize: size_t
    maxObjectSize: size_t
    
    
    FixedAllocator
    chunks: vector<Chunk>
    allocChunk_: Chunk*
    deallocChunnk_: Chunk*
    
    Chunk
    pData_: unsigned char*
    firstAvailableBlock_: unsigned char
    blocksAvailabel_: unsigned char
    

    loki allocator, Chunk

    void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks){
        pData_ = new unsigned char[blockSize * blocks];
        Reset(blockSize, blocks);
    }
    void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks){
        firstAvailableBlock_ = 0;
        blocksAvailable_ = blocks;
        unsigned char i = 0;
        unsigned char * p = pData_;
        for(;i != blocks; p += blockSize) *p = ++ i;
    }
    void FixedAllocator::Chunk::Release()
    {
        delete[] pData;
    }
    void * FixedAllocator::Chunk::Allocate(std::size_t blockSize){
        if(!blocksAvailable_) return 0;
        unsigned char * pResult = 
        pData_ + (firstAvailableBlock_ * blockSize);//这块内存还没分配出去的时候,里面会存放下一块的地址
        firstAvailableBlock_ = *pResult;//那么令当前值为下一块的地址
        -- blocksAvailable_;
        return pResult;
    }
    void FixedAllocator::Chunk::Deallocate(void * p, std::size_t blockSize){
        unsigned char* toRelease = static_cast<unsigned char*>(p);
        *toRelease = firstAvailableBlock_;//先让toRelease即归还值的空间指向原来的第一块要分配的内存的地址
        firstAvailableBlock_ = static_cast<unsigned char>((toRelease - pData_) / blockSize);//接下来分配的第一块的空间
        ++ blocksAvailable_;
    }//说句实在话,这显然就是个并查集,不懂的同学可以去学习一下
    void * FixedAllocator::Allocate(){
        if(allocChunk == 0 || allocChunk -> blocksAvailble_ == 0){
            Chunks::iterator i = chunks_begin();
            for(;; ++ i){
                if(i == chunks_.end()){
                    chunks_.push_back(Chunk());
                    Chunk& newChunk = chunks_.back();
                    newChunk.Init(blockSize_.numBlocks_);
                    allocChunk_ = &newChunk;
                    deallocChunk_ = &chunks_.front();
                    break;
                }
                if(i -> blocksAvailable_ > 0){
                    allocChunk_ = &*i;
                    break;
                }
            }
        }
        return allocChunk_->Allocate(blockSize_);
    }
    void FixedAllocator::Deallocate(void * p){
        deallocChunk_ = VicinityFind(p);
        DoDeallocate(p);
    }
    FixedAllocator::Chunk * FixedAllocator::VicinityFind(void * p){
        const std::size_t chunkLength = numBlocks_ * blockSize_;
    
        Chunk* lo = deallocChunk_;
        Chunk* hi = deallocChunk_ + 1;
        Chunk* loBound = & chunks_.front();
        Chunk* hiBound = & chunks_.back() + 1;
        for(;;){
            if(lo){
                if(p >= lo->pData_ && p<lo -> pData_ + chunkLength)   return lo;
                if(lo == loBound) lo = 0;
                else -- lo;
            }
            if(hi){
                if(p >= hi->pData_ && p < hi -> pData_ + chunkLength)   return hi;
                iif(++ hi == hiBound) hi = 0;
            }
            if(hi == 0 && lo == 0){
                return 0;
            }//通过加入这个防止,丢入一个外界的指针 导致死循环
        }
        return 0;
    }
    
    void FixedAllocator::DoDellocate(void * p){
        deallocChunk_->Deallocate(p, blockSize_);
        if(deallocChunk_->blocksAvailable_ == numBlocks_){
            Chunk& lastChunk = chunks_.back();
            if(&lastChunk == deallocChunk_){
                if(chunks_.size() > 1 &&
                deallocChunk_[-1].blocksAvailable_ == numBlocks_){
                    lastChunk.Release();
                    chunks_.pop_back();
                    allocChunk_= deallocChunk_ = &chunks_.front();
                }
                return ;
            }
            if(lastChunk.blocksAvailable_ == numBlocks_){
                lastChunk.Release();
                chunks_.pop_back();
                allocChunk_ = dealoocChunk_;
            }
            else{
                std::swap(*deallocChunk_, lastChunk);
                allocChunk_ = &chunks_.back();
            }
        }
    }
    

    loki allocator怎么说呢?
    就是手段暴力,采用并查集实现手法妙
    记录可用区块有多少个
    存在deferring能力
    23333 本身alloctor就是支撑容器的分配器 本身的结构使用了vector 很有意思的

    GNU C++

    template<class T, 
    class Allocator = allocator<T>>
    class vector;
    //大概的格式都如上
    
    __gnu_cxx::new_allocator
    template<typename _Tp>
    class new_allocator{
        .....
        pointer allocate(size_type__n, const void
        * = 0){
            return static_cast<_Tp*>
            (::operator new(__n * sizeof(_Tp)));
        }
        void deallocate(pointer __p, size_type){
            ::operator delete(__p);
        }
    };
    __gnu_cxx::malloc_allocator
    template<typename _Tp>{
        pointer allocate(size_type __n, const void
        * = 0){
            pointer __ret = .......(std::malloc(
                __n * sizeof(_Tp)
            ));
        }
        void deallocate(pointer __p, size_type){
            std::free(......(__p));
        }
    };
    //存在另一种做法智能型allocator
    //存在两种实现 bitmap index; fixed-size pooling cache
    __gnu_cxx::bitmap_allocator
    __gnu_cxx::pool_allocator
    __gnu_cxx::__mt_alloc
    __gnu_cxx::debug_allocator
    //外覆器(记录size)
    __gnu_cxx::array_allocator
    //允许分配已知且固定大小的内存块
    //内存来自std::array pbject
    
    
    //vs2013 new_allocator
    template<class _Ty>
    class allocator:public _Allocator_base<_Ty>
    {
    public:
        typedef value_type *pointer;
        typedef size_t size_type;
        void deallocate(pointer _Ptr, size_type){
            ::operator delete(_Ptr);
        }
        pointer allocate(size_type _Count){
            return _Allocate(_Count,(pointer)0);
        }
        pointer allocate(size_type _Count,
        const void *){
            return (allocate(_Count));
        }
    ....
    };
    
    //G4.9 标准分配器
    template<typename _Tp>
    class new_allocator{
        pointer allocate(size_type __n, const void*
        = 0){
            if(__n > this -> max_size())
            std::__throw_bad_alloc();
            return static_cast<_Tp*>
            (::operator new(__n * sizeof(_Tp)));
        }
        void deallocate(pointer __p, size_type){
            ::operator delete(__p);
        }
    }
    #define __allocator_base __gnu_cxx::new_allocator
    class allocator:public __allocator_base<_Tp>{}
    ;
    template<typename_Tp>class
    malloc_allocator{
        pointer
        allocate(size_type __n,const void * = 0){
            if(__n > this->max_size())
            std::__throw_bad_alloc();
            pointer __ret = static_cast<_Tp*>
            (std::malloc(__n * sizeof(_Tp)));
            if(! __ret){
                std::__throw_bad_alloc();
                return __ret;
            }
        }
        void deallocate(pointer __p, size_type)
        {
            std::free(static_cast<void*>
            (__p));
            size_type
            max_size() const _GLIBCXX_USE_NOEXCEPT{
                return size_t(-1) / sizeof(_Tp);
            }
        }
    }
    template<typename _Tp, typename _Array = 
    std::tr1::array<_Tp, 1>>
    class array_allocator:public array_allocator_base
    <_Tp>{
    public:
        typedef size_t size_type;
        typedef _Tp value_type;
        typedef _Array array_type;
    private:
        array_type * _M_array;
        size_type _M_used;
    public:
        array_allocator(array_type * __array = 
        NULL)   throw()
        :_M_array(__array),_M_used(size_type()){}
    }//保留了deallocate,因为是静态的所以无所谓释放
    //使用操作如下
    int my[65536];
    array_allocator<int, array<int, 65536>>
    myalloc(&my);
    template<typename _Alloc>
    class debug_allocator{
    pritvate:
        size_type _M_extra;
        _Alloc _M_allocator;
        size_type _S_extra(){
            const size_t __obj_size = sizeof(value
            _type);
            return (sizeof(size_type) +
            __obj_size - 1) / __obj_size;
        }
    public:
        debug_allocator(const _Alloc& __a):
        _M_allocator(__a), _M_extra(_S_extra()){}
    }
    template<typename _Tp>
    class bitmap_allocator:private free_list{
    public:
        pointer allocate(size_type __n){
            if(__n > this -> max_size()){
                std::__throw_bad_alloc();
            }
            if(__builtin_expect(__n == 1, true))
            return this->_M_allocate_single_object();
            else{
                const size_type __b = __n * 
                sizeof(value_type);
                return reomterpret_cast<pointer>
                (::operator new(__b));
            }
        }
        void deallocate(pointer __p, size_type __n)
        throw(){
            if(__builtin_expect(__p != 0, true)){
                if(__builtin_expect(__n == 1, 
                true))
                this -> _M_deallocate_single_object
                (__p);
                else{
                    ::opertor delete(__p);
                }
            }
        }
    }
    //一次挖64个区块来供应加上bitmap == super-blocks
    //64个bit使用两个unsiged int来代表bitmap
    //还有usecount来计入使用了多少内存
    //使用一个单元指向最前面跟最后面的元素
    //即__mini_vector 大概是三根指针
    //_M_start, _M_finish, _M_end_of_storage
    //bitmap跟blocks相反
    //即将全为1的bit的最后一位变成0
    //最后4位1110
    //如果1st super-block 用尽,则使用2nd super-block
    //2ed super-block大小是128bytes
    //即64 * 2
    //3rd 就是128 * 2
    
    
    //bitmap_allocator全回收
    //使用一个mini_vector去指向头
    
  • 相关阅读:
    HTML 块级、内联、内联块级元素(转)
    CSS margin-top父元素下落
    JS 窗口resize避免触发多次
    ASP.NET 自制免费.NET代码生成器KevinCodeBuilder
    Plugin 中国省市选择插件
    Git 常用命令(转)
    JS Regex正则表达式的使用(转)
    Git 详细中文安装教程(转)
    业务逻辑中的测试总结(一)----比值类需求测试分解
    python学习笔记系列----(四)模块
  • 原文地址:https://www.cnblogs.com/qq136155330/p/12490019.html
Copyright © 2020-2023  润新知