为什么要有空间配置器?
1.内存碎片问题(外碎片)
由于频繁分配、释放小块内存容易在堆中造成外碎片(极端情况下就是堆中空闲的内存总量满足一个请求,但是这些空闲的块都不连续,导致任何一个单独的空闲的块都无法满足这个请求)。
2.频繁的分配小块内存,效率比较低。
开辟空间的时候,分配器去找一块空闲块给用户。找空闲块也是需要时间的,尤其是在外碎片比较多的情况下。
如果分配器其找不到,就要考虑处理假碎片现象(释放的小块空间没有合并),这时候就要将这些已经释放的的空闲块进行合并,这也是需要时间的。
malloc在开辟空间的时候,这些空间会带有一些附加的信息,这样的话也就造成了空间的利用率有所降低,尤其是在频繁申请小块内存的时候。
STL中的空间配置器分为两种:
一级空间配置器
二级空间配置器
在STL中默认如果要分配的内存大于128个字节的话就是大块内存,调用一级空间配置器直接向系统申请。
如果小于等于128个字节的话则认为是小内存,则就去内存池中申请。一级空间配置器很简单,直接封装了malloc和free处理,增加_malloc_alloc_oom_handle处理机制。二级空间配置器才是STL的精华,二级空间配置器主要由内存池和自由链表构成。
框架设计
流程及代码实现
“内存不足处理机制”是客户端的责任,设置”内存不足处理介质”也是客户端的责任。
//一级空间配置器
template <int inst>
class __MallocAllocTemplate
{
public:
//static void(*__malloc_alloc_oom_handler)();
static HANDLE_FUNC __malloc_alloc_oom_handler; //HANDLE_FUNC 代表一个函数指针
static void* OOM_Malloc(size_t n)
{
while (1)
{
if (__malloc_alloc_oom_handler == 0)
throw bad_alloc();
__malloc_alloc_oom_handler(); // 释放内存;如果设置了句柄,那么就会一直循环,直到申请到了,才会退出
void* ret = malloc(n);
if (ret)
return ret;
}
}
static void* Allocate(size_t n)
{
void* ret = malloc(n);
if (ret == NULL) //一级空间配置器没有申请到内存
{
ret = OOM_Malloc(n);
}
return ret;
}
static void Deallocate(void* p, size_t n)
{
free(p);
}
static HANDLE_FUNC SetMallocHandler(HANDLE_FUNC f)
{
HANDLE_FUNC old = f;
__malloc_alloc_oom_handler = f;
return old;
}
};
//二级空间配置器
template <bool threads, int inst>
class __DefaultAlloceTemplate
{
public:
static size_t FREELIST_INDEX(size_t bytes)
{
return ((bytes + __ALIGN - 1) / __ALIGN - 1); //找到链接的index
}
static size_t ROUND_UP(size_t bytes)
{
return ((bytes)+__ALIGN - 1)&(~(__ALIGN - 1));//变成8的倍数
}
//在内存池中申请对象
static char* Chunk(size_t n, size_t &nobjs)
{
size_t needBytes = n * nobjs; //需要的大小(即20块内存)
size_t leftBytes = _endfree - _startfree; //内存池中剩余的大小
//内存池中的内存足够申请n*nobjs大小的内存
if (needBytes <= leftBytes) //内存池中剩余的内存足够申请20块
{
char* ret = _startfree;
_startfree += needBytes;
return ret;
}
//虽然不够申请needBytes,但是足够申请n大小的内存
else if (leftBytes > n)
{
nobjs = leftBytes / n; //计算内存池中剩余的内存足够申请多少块
needBytes = n*nobjs;
char* ret = _startfree;
_startfree += needBytes;
return ret;
}
//剩余的内存连1个都申请不到
else
{
if (leftBytes > 0) //虽然不够申请一个,但是内存池中还有内存
{
size_t index = FREELIST_INDEX(leftBytes); //将剩下的内存挂入自由链表
((Obj*)_startfree)->free_list_link = _freeList[index];
(_freeList[index])->free_list_link = (Obj*)_startfree;
}
size_t byteToGets = needBytes * 2 + ROUND_UP(_heapsize >> 4); //malloc内存
_startfree = (char*)malloc(byteToGets);
if (_startfree == NULL) //malloc失败了
{
size_t index = FREELIST_INDEX(n); //去index后面的自由链表中找比它大的内存块,找到则放入内存池
for (size_t i = index; i < __NFREELISTS; ++i)
{
if (_freeList[i])
{
_startfree = (char*)_freeList[i];
_freeList[i] = ((Obj*)_startfree)->free_list_link;
_endfree = _startfree + (i + 1)*__ALIGN;
return Chunk(n, nobjs);
}
}
_endfree = 0; //自由链表中也没有更大的内存块了,调用一级空间配置器
_startfree = (char*)__MallocAllocTemplate<0>::Allocate(byteToGets);
}
_heapsize = _heapsize + byteToGets;
_endfree = _startfree + _heapsize;
return Chunk(n, nobjs);
}
}
//填充自由链表
static char* Refill(size_t n)
{
size_t nobjs = 20; //一次申请二十个对象
char* ret = Chunk(n, nobjs);
if (nobjs == 1) //nobjs=1说明只从内存池中拿到了一块内存,直接返回
{
return ret;
}
else
{
size_t index = FREELIST_INDEX(n);
_freeList[index] = (Obj*)(ret + n); //把剩下的内存块挂入自由链表
Obj* cur = _freeList[index];
for (size_t i = 1; i < nobjs; ++i)
{
Obj* next = (Obj*)((char*)cur + n);
cur->free_list_link = next;
cur = next;
}
cur->free_list_link = NULL;
return ret;
}
}
static void* Allocate(size_t n)
{
if (n > __MAX_BYTES)
{
return __MallocAllocTemplate<0>::Allocate(n); //如果申请的内存块大于128,则直接调用一级空间配置器的Allocate
}
size_t index = FREELIST_INDEX(n); //计算出index
if (_freeList[index]) //index下面挂的有所需内存块
{
Obj* ret = _freeList[index];
_freeList[index] = ret->free_list_link;
return ret;
}
else
return Refill(ROUND_UP(n)); //index下面没有所需内存块,则开始填充自由链表
}
static void Deallocate(void* ptr, size_t n)
{
if (n > __MAX_BYTES)
{
__MallocAllocTemplate<0>::Deallocate(n);
}
size_t index = FREELIST_INDEX(n); //将还回来的内存挂入自由链表
Obj* obj = (Obj*)ptr;
obj->_freeListLink = _freeList[index];
_freeList[index] = obj;
}
private:
enum { __ALIGN = 8 };
enum { __MAX_BYTES = 128 };
enum { __NFREELISTS = __MAX_BYTES / __ALIGN };
union Obj {
union Obj * free_list_link;
char client_data[1]; /* The client sees this. */
};//管理自由链表
//狭义内存池
static Obj*_freeList[__NFREELISTS];
static char*_startfree;
static char*_endfree;
static size_t _heapsize;
};
template <bool threads, int inst>
char* __DefaultAlloceTemplate<threads, inst>::_startfree = 0;
template <bool threads, int inst>
char* __DefaultAlloceTemplate<threads, inst>::_endfree = 0;
template <bool threads, int inst>
size_t __DefaultAlloceTemplate<threads, inst>::_heapsize = 0;
template <bool threads, int inst>
typename __DefaultAlloceTemplate<threads, inst>::Obj*
__DefaultAlloceTemplate<threads, inst>::_freeList[__NFREELISTS] = { 0 };