STL一级空间配置器
原创
©著作权归作者所有:来自51CTO博客作者honeyR的原创作品,请联系作者获取转载授权,否则将追究法律责任
一、什么是STL
STL(standard Template Library,标准模板库),从根本上说,STL是一些“容器”的集合,这些“容器”有list,vector,set,map等。
STL也是算法和其他一些组件的集合。
STL有六大组件,彼此可以组合套用,六大组件分别是:
1、容器(containers):各种数据结构,如vector,list,deque,set,map等,用来存放数据。
2、算法(algorithms):各种算法如sort,search,copy,erase等。
3、迭代器(iterators):扮演容器与算法之间的胶合剂,共有五种类型。
4、仿函数(functors):行为类似函数,可作为算法的某种谋略。
5、适配器(adapters):一种用来修饰容器或仿函数或迭代器接口的东西。
6、配置器(allocators):负责空间配置与管理。
我们之前经常接触容器与容器适配器,来复习一下吧!
1、容器
(1)序列式容器
比如:
向量(vector) :连续存储的元素
列表(list) :由节点组成的双向链表,每个结点包含着一个元素
双端队列(deque) :连续存储的指向不同元素的指针所组成的数组
(2)关联式容器
比如:
集合(set):由节点组成的红黑树,每个节点都包含着一个元素,节点之间以某种作用于元素对的谓词排列,没有两个不同的元素能够拥有相同的次序
多重集合(multiset):允许存在两个次序相等的元素的集合
映射(map):由{键,值}对组成的集合,以某种作用于键对上的谓词排列
多重映射(multimap):允许键对有相等的次序的映射
2、容器适配器
栈(stack):后进先出的值的排列
队列(queue):先进先出的值的排列
优先队列(priority_queue):元素的次序是由作用于所存储的值对上的某种谓词决定的的一种队列
二、空间配置器
1、什么是空间配置器
所谓空间适配器,就是用来管理内存的一个器具。对于STL来说,空间适配器是它可以正常工作的基础,也为它可以高效工作提供了动力。对于使用STL来说,它是不和用户直接打交道的,而是隐藏在一切STL组建之后,默默为各种内存申请提供支持的。
注:这里所指的空间不单单指内存,也可以是磁盘或者其他存储介质。
2、空间配置器有什么好处
- 提高代码复用率,功能模块化
- 减少内存碎片问题
- 效率问题:频繁的分配小块内存,效率比较低
- 容易造成内存泄露
- 调用malloc/ new向系统分配的每块内存都有一些额外的开销
- 空间不足时应对措施
- 隐藏实际中对存储空间的分配及释放的细节&确保所有被分配的存储空间都最终能获得释放
3、空间配置器的思想
对于我们来说,对new和delete很熟悉,这两个函数可以分别完成内存的申请和释放,和c里面的malloc和free如出一辙。
Std::alloc的主要思想是:
(1)定义一个空间大小阈值,128bytes;
(2)如果申请的空间大于128bytes,那么就调用第一级空间适配器来完成分配工作;
(3)如果小于128bytes,那么就调用第二级空间适配器来完成。
3、一级空间配置器
(1)对于一级空间适配器,直接调用malloc和free来完成分配与释放内存的工作(没有调用new和delete)。
(2)一级适配器具有new-handle机制,用户可以指定当出现out-of-memory时的自定义内存不足处理例程。
(3)当第一级alloc失败时,会接着调用oom_alloc函数来尝试分配内存,如果oom发现没有指定new-handler函数的话,会抛出__THROW_BAD_ALLOC这个异常。
(4)利用malloc重新配置空间
(5)realloc与alloc过程相似。
下面是第一级适配器的源码:
// 一级空间配置器
template <int __inst>
class __malloc_alloc_template {
private:
//以下函数用来处理内存不足的情况
static void* _S_oom_malloc(size_t);
static void* _S_oom_realloc(void*, size_t);
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
//内存不足处理例程
static void(*__malloc_alloc_oom_handler)(); //__malloc_alloc_oom_handler是函数指针
#endif
public:
//空间配置函数
static void* allocate(size_t __n)
{
//用malloc申请空间
void* __result = malloc(__n);
//如果申请空间失败,则调用_S_oom_malloc重新申请空间
if (0 == __result) __result = _S_oom_malloc(__n);
return __result;
}
//空间释放函数
static void deallocate(void* __p, size_t /* __n */)
{
//调用free来释放空间
free(__p);
}
//空间重配置函数
static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz)
{
//用realloc来重新配置空间
void* __result = realloc(__p, __new_sz);
//如果由于空间不足配置失败,则调用_S_oom_realloc重新申请空间
if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
return __result;
}
//该函数重新指定了内存分配异常处理函数,并返回了原有的内存分配异常处理函数
//该函数接受一个“返回值和参数均为空的函数指针”作为参数
//该函数最后返回一个“返回值和参数均为空的函数指针”
static void(*__set_malloc_handler(void(*__f)()))()
{
void(*__old)() = __malloc_alloc_oom_handler;
//重新指定内存分配异常处理函数
__malloc_alloc_oom_handler = __f;
//返回原有的内存分配异常处理函数
return(__old);
}
};
// malloc_alloc out-of-memory handling
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int __inst>
//内存不足处理例程,初始值为0,待用户自定义,考虑内存不足时的应变措施
void(*__malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif
template <int __inst>
//申请空间失败时调用_S_oom_malloc重新申请空间
void* __malloc_alloc_template<__inst>::_S_oom_malloc(size_t __n)
{
//定义一个函数指针__my_malloc_handler
void(*__my_malloc_handler)();
void* __result;
//该循环有两个退出条件
//一个是成功申请到空间,最终返回__result
//另一个是最终申请空间失败,抛出异常__THROW_BAD_ALLOC
for (;;) {
__my_malloc_handler = __malloc_alloc_oom_handler;
//由于初值为0,如果用户没有自定义内存不足处理例程,那么还是抛出异常
if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
//调用内存不足处理例程,尝试释放空间
(*__my_malloc_handler)();
//再次尝试配置空间
__result = malloc(__n);
if (__result) return(__result);
}
}
template <int __inst>
//重新配置空间失败时调用_S_oom_realloc申请空间
void* __malloc_alloc_template<__inst>::_S_oom_realloc(void* __p, size_t __n)
{
//定义一个函数指针__my_malloc_handler
void(*__my_malloc_handler)();
void* __result;
//该循环有两个退出条件
//一个是成功申请到空间,最终返回__result
//另一个是最终申请空间失败,抛出异常__THROW_BAD_ALLOC
for (;;) {
__my_malloc_handler = __malloc_alloc_oom_handler;
//由于初值为0,如果用户没有自定义内存不足处理例程,那么还是抛出异常
if (0 == __my_malloc_handler) { __THROW_BAD_ALLOC; }
//调用内存不足处理例程,尝试释放空间
(*__my_malloc_handler)();
//再次尝试配置空间
__result = realloc(__p, __n);
if (__result) return(__result);
}
}
//由于参数inst在这里没有排上用场,因此定义为数值0
typedef __malloc_alloc_template<0> malloc_alloc;
通过对源码的理解,也不难模拟出来一个一级空间配置器:
//一级空间配置器 >128
//函数指针
typedef void(*pMallocHandle)();
template<int inst>
class MallocAllocTemplate
{
public:
void* Allocate(size_t size)
{
void* result = malloc(size);
//申请失败调用OOM_Malloc()函数
if (NULL == result)
{
result = OOM_Malloc(size);
}
return result;
}
//重新分配空间
void* ReAllocate(void* p,size_t,size_t newSize) //三个参数
{
void* result = realloc(p, newSize);
if (NULL == result)
{
result = OOM_Realloc(p, newSize);
}
return result;
}
void DeAllocate(void* p,size_t) //底层是两个参数
{
free(p);
}
private:
void* OOM_Malloc(size_t size)
{
pMallocHandle mallocHandle;
void* res;
for (;;)
{
mallocHandle = _mallocHandle;
if (NULL == mallocHandle)
throw std::bad_alloc();
//尝试去释放已经获取,但是不用的堆空间
mallocHandle();
//尝试重新分配内存
res = malloc(size);
if (res)
return res;
}
}
void* OOM_Realloc(void *p, size_t size)
{
pMallocHandle mallocHandle;
void* res;
for (;;)
{
mallocHandle = _mallocHandle;
if (NULL == mallocHandle)
throw std::bad_alloc();
//尝试去释放已经获取但是不用的堆空间
mallocHandle();
res = realloc(p, size);
if (res)
return res;
}
}
//若申请失败,释放方法
pMallocHandle SetMallocHandle(pMallocHandle mallocHandle)
{
pMallocHandle old = _mallocHandle;
_mallocHandle = mallocHandle;
return old;
}
private:
static pMallocHandle _mallocHandle;
};
pMallocHandle MallocAllocTemplate<0>::_mallocHandle = NULL;