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
内存池目标:
- 速度
- 空间
写一个分配器
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只有两个选择
- 让更多内存可用
- 调用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
可以通过实验发现,alloc分配的内存大于常规分配器
当然我的猜想是,可能pool里面有剩余的内存没被使用
因为每次要的空间都是40倍 + GOUND_UP(X >> 4)
malloc/free详解
新版本废除了SBH系列的函数
malloc里精巧设计SBH
- _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;
}
- _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
- _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
);
- _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);
- __sbh_alloc_block()
sizeEntry = (intSize + 2 * sizeof(int)
+ (BYTES_PER_PARA - 1)
)
& ~(BYTES_PER_PARA - 1);
//起到的功能是调整到16的倍数
- __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去指向头