STL空间配置器的底层原理:
维护了一个狭义的内存池,并且用一个自由链表来维护该内存池。该自由链表比较类似于哈希的开链法的存储结构。
源代码:
#pragma once
using namespace std;
#define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)
typedef void*(*FUNC_PTR)();
template <int inst>
class __MallocAllocTemplate //一级空间配置器
{
public:
static void* Allocate(size_t n)
{
void* result = malloc(n);
if (result == 0)
{
result = OomMalloc(n);
}
return result;
}
static void Deallocate(void* p)
{
free(p);
}
static void* Reallocate(void* p, size_t n)//n代表新的空间大小
{
void* result = realloc(n);
if (result == 0)
{
result = OomRealloc(p, n);
}
return result;
}
static FUNC_PTR SetMallocHandler(FUNC_PTR f) //static void (* SetMallocHandler(void (*f)()))()
{
FUNC_PTR old = __mallocAllocOomHandler;
__mallocAllocOomHandler = f;
return old;
}
private:
//函数声明
static void* OomMalloc(size_t);
static void* OomRealloc(void *, size_t);
//静态成员变量
static FUNC_PTR __mallocAllocOomHandler;
};
//静态变量的初始化
template<int inst>
FUNC_PTR __MallocAllocTemplate<inst>::__mallocAllocOomHandler = 0;
template<int inst>
void* __MallocAllocTemplate<inst>::OomMalloc(size_t n)
{
FUNC_PTR myMallocHandler;
void* result;
while (1)
{
myMallocHandler = __mallocAllocOomHandler;
if (0 == myMallocHandler)
__THROW_BAD_ALLOC;//抛出异常
myMallocHandler();
result = malloc(n);
if (result)
return result;
}
}
template<int inst>
void* __MallocAllocTemplate<inst>::OomRealloc(void* p, size_t)
{
FUNC_PTR myMallocHandler;
void* result;
while (1)
{
myMallocHandler = __mallocAllocOomHandler;
if (0 == myMallocHandler)
_THROW_BAD_ALLOC;//抛出异常
myMallocHandler();
result = realloc(p, n);
if (result)
return result;
}
}
typedef __MallocAllocTemplate<0> MallocAlloc;
template <bool threads, int inst> //二级空间配置器
class __DefaultAllocTemplate
{
public:
static void* Allocate(size_t n)
{
obj** myFreeList;
obj* result;
if (n > __MAX_BYTES) //如果大小超过128直接调用一级空间配置器(认为超过128的都是大内存)
{
return MallocAlloc::Allocate(n);
}
myFreeList = _freeList + FREELIST_INDEX(n);
result = *myFreeList;
if (result == NULL)//代表该位置还没挂有空闲的空间,然后去申请
{
void* r = _Refill(ROUND_UP(n));//n需要8字节对齐
return r;
}
else
{
//头删
*myFreeList = result->_freeListLink;
return result;
}
}
static void Deallocate(void* p, size_t n)
{
obj** myFreeList;
if (n > __MAX_BYTES)
{
MallocAlloc::Deallocate(p);
}
myFreeList = _freeList + FREELIST_INDEX(n);
//头插
((obj*)p)->_freeListLink = *myFreeList;
*myFreeList = (obj*)p;
}
private:
static size_t ROUND_UP(size_t bytes) //8字节向上对齐
{
return (((bytes)+__ALIGN - 1) & ~(__ALIGN - 1));
}
static size_t FREELIST_INDEX(size_t bytes)//定位
{
return (((bytes)+__ALIGN - 1) / __ALIGN - 1);
}
private:
//变量
enum { __ALIGN = 8 }; //默认最小的内存块是8个字节
enum { __MAX_BYTES = 128 };//默认最大的内存块是128个字节
enum { __NFREELISTS = __MAX_BYTES / __ALIGN };//自由链表的个数
union obj
{
union obj* _freeListLink;
char _clientData[1];
};
static char* _startFree; //内存池的头指针
static char* _endFree; //内存池的头尾指针
static size_t _heapSize; //内存池总共有多少空间
static obj* _freeList[]; //指针数组(自由链表的指针数组)
//函数
static void* _Refill(size_t n); //对大块内存进行分割并且挂在自由链表下
static char* _ChunkAlloc(size_t size, int &nobjs);//开辟大块内存
};
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;
template <bool threads, int inst>
typename __DefaultAllocTemplate<threads, inst>::obj* __DefaultAllocTemplate<threads, inst>::_freeList[] = { NULL };
//此处加typename是因为obj是类中的一个类型,要找到它必须先找到__DefaultAllocTemplate<threads, inst>,
//如果不加typename编译器将无法找到__DefaultAllocTemplate<threads, inst>这个类型中的obj类型
//其目的是为了申明__DefaultAllocTemplate<threads, inst>是一个类型
//填充自由链表
template <bool threads, int inst>
void* __DefaultAllocTemplate<threads, inst>::_Refill(size_t n) //将分配好的大块内存的第一个返回,将剩下的挂起来
{
int nobjs = 20;
char* chunk = _ChunkAlloc(n, nobjs);//申请大块内存
obj** my_free_list;
obj* cur;
obj* next;
obj* result;
if (nobjs == 1)
return chunk;
result = (obj*)chunk; // 将第一个chunk返回
my_free_list = _freeList + FREELIST_INDEX(n); //定位
*my_free_list = next = (obj*)(chunk + n);//将第二个chunk加入到自由链表中
//将剩下的挂起来
for (int i = 2; i < nobjs; ++i) // 第一个返回,第二个被填充在指针数组中,所以从2开始全部挂起来
{
cur = next;
next = (obj*)((char*)next + n);
//尾插
cur->_freeListLink = next;
}
next->_freeListLink = NULL;
return result;
}
//进行内存块的分配
template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_ChunkAlloc(size_t size, int &nobjs) //分配大块内存
{
size_t BytesLeft = _endFree - _startFree; //内存池剩余的空间大小
size_t TotelBytes = size*nobjs;//当前需要的申请的内存大小
char* result;
if (BytesLeft >= TotelBytes) // 剩余的空间足够
{
result = _startFree;
_startFree += TotelBytes;
return result;
}
else if (BytesLeft >= size)//剩余的空间不够20个,但是够1个以上
{
nobjs = BytesLeft / size;//计算新的能够申请的最大个数
TotelBytes = size*nobjs;//计算新的需要的空间大小
result = _startFree;
_startFree += TotelBytes;
return result;
}
else //剩余的空间还不够一个size
{
//先将内存池中剩余的一点空间挂起来
if (BytesLeft > 0) //还有一点剩余
{
obj** myFreeList = _freeList + FREELIST_INDEX(TotelBytes);
((obj*)_startFree)->_freeListLink = *myFreeList;
*myFreeList = (obj*)_startFree;
}
//申请空间
size_t bytesToGet = TotelBytes * 2 + ROUND_UP(_heapSize / 16); //计算新申请空间的大小
_startFree = (char*)malloc(bytesToGet); //申请空间
//申请空间失败,去自由链表里边找更大的内存
if (_startFree == 0)
{
int i = FREELIST_INDEX(size)+1;
for (; i <= __NFREELISTS; ++i)
{
if (_freeList[i] != NULL)//如果找到,则将这块内存交给内存池来管理分配
{
_startFree = (char*)_freeList[i];
_freeList[i] = _freeList[i]->_freeListLink;
_endFree = _startFree + (i+1)*__ALIGN;
return _ChunkAlloc(size, nobjs);
}
}
//山穷水尽
_endFree = 0;
_startFree = (char*)MallocAlloc::Allocate(bytesToGet); //如果还失败,_startFree接受一个值为0
}
//空间申请成功
_heapSize += bytesToGet;
_endFree = _startFree + bytesToGet;
return _ChunkAlloc(size, nobjs);
}
}
#ifdef __USE_MALLOC
typedef MallocAlloc alloc;
#else
typedef __DefaultAllocTemplate<false, 0> alloc;
#endif //__USE_MALLOC
template<class T, class Alloc>
class SimpleAlloc
{
public:
static T* Allocate(size_t size)
{
return 0 == size ? 0 : Alloc::Allocate(size*sizeof(T));
}
static T* Allocate(void)
{
return (T*)Alloc::Allocate(sizeof(T));
}
static void Deallocate(T* p, size_t size)
{
if (0 != size)
{
Alloc::Deallocate(p, size*sizeof(T));
}
}
static void Deallocate(T *p)
{
Alloc::deallocate(p, sizeof(T));
}
};
#include<vector>
using namespace std;
void test()
{
SimpleAlloc<int, alloc> sa;
vector<int*> v;
for (int i = 0; i < 20; ++i)
{
v.push_back(sa.Allocate());
}
v.push_back(sa.Allocate());
}