这篇文章主要介绍的是STL中空间配置器的底层实现。。。
1、为什么要存在空间配置器?
为什么要有空间配置器呢?这主要是从两个方面来考虑的。
1、小块内存带来的内存碎片问题
单从分配的角度来看。由于频繁分配、释放小块内存容易在堆中造成外碎片(极端情况下就是堆中空闲的内存总量满足一个请求,但是这些空闲的块都不连续,导致任何一个单独的空闲的块都无法满足这个请求)。
2、小块内存频繁申请释放带来的性能问题。
关于性能这个问题要是再深究起来还是比较复杂的,下面我来简单的说明一下。
开辟空间的时候,分配器会去找一块空闲块给用户,找空闲块也是需要时间的,尤其是在外碎片比较多的情况下。如果分配器其找不到,就要考虑处理假碎片现象(释放的小块空间没有合并),这时候就要将这些已经释放的的空闲块进行合并,这也是需要时间的。
malloc在开辟空间的时候,这些空间会带有一些附加的信息,这样的话也就造成了空间的利用率有所降低,尤其是在频繁申请小块内存的时候。
频繁的申请小额空间,开销非常大。
STL中使用的是空间配置器来解决上面的问题。。。
2、STL中的空间配置器
STL中的空间配置器主要分为两级,一级空间配置器(__MallocAllocTemplate
)和二级空间配置器(__DefaultAllocTemplate
)。
1>、一级空间配置器
在SGI版的一级空间配置器中它的Allocate()直接使用malloc,Deallocate()直接使用free,相当于就是对malloc,free做了一层最简单的封装,一级空间配置器还提供了__Malloc_Alloc_OOM_Handler
来处理内存不足的情况,如果没有设置此时就会抛出bad_alloc的异常,否则就会循环开辟空间,直到开辟成功才返回。
一级空间配置器的流程图:
SGI以malloc来配置内存。当malloc()失败后,就调用oom_alloc(),如果客户端没有设置内存不足处理机制,则就直接抛出bad_alloc异常信息,或者直接终止程序。如果客户端设置了内存不足处理机制,则他就会一直调用内存处理机制,企图在某次调用之后获得一块足够的内存。但是如果内存不足处理机制设计不好的话,存在死循环的危险。
直接调用malloc和free来配置释放内存,简单明了。
typedef void(*MALLOCALLOC)(); //将void (*)() 重命名成MALLOCALLOC
/* oom_alloc为静态函数成员,用于处理malloc时的内存不足问题
_malloc_alloc_handler为静态数据成员,为void(*)()类型的函数指针,用于用户自己制定内存分配策略
*/
template<int inst>
class _MallocAllocTemplate
{
private:
static void* _OomMalloc(size_t); //malloc失败的时候调用的函数
static MALLOCALLOC _MallocAllocOomHandler; //函数指针,内存不足的时候的处理机制
public:
static void* _Allocate(size_t n) //分配空间n个字节的空间
{
void *result=0;
result = malloc(n);
if (0 == result) //若果malloc失败,则就调OOM_malloc
_OomMalloc(n);
return result;
}
static void _DeAllocate(void *p) //释放这块空间
{
free(p);
}
/*此静态成员函数接受一个void(*)()类型的函数指针作为参数,返回
void(*)()类型的函数指针。其作用为用用户自己定制的内存调度方法替换
_malloc_alloc_handler,由此实现类似C++的set_new_handler方法。
*/
static MALLOCALLOC _SetMallocHandler(MALLOCALLOC f) //这是一个函数,参数是一个函数指针,返回值也是一个函数指针
{
MALLOCALLOC old = _MallocAllocOomHandler;
_MallocAllocOomHandler = f; //将内存分配失败的句柄设置为f(让它指向一个内存失败了,让系统去释放其他地方空间的函数)
return old;
}
};
template<int inst>
void(* _MallocAllocTemplate<inst>::_MallocAllocOomHandler)()=0; //默认不设置内存不足处理机制
template<int inst>
void* _MallocAllocT