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去指向头
posted @ 2020-03-13 23:52  moxin0509  阅读(556)  评论(0编辑  收藏  举报