STL源码剖析(二)第一级空间配置器
一、空间配置器的作用
在学习空间配置器之前,我们需要先知道它是用来干嘛的,简单的说,它就是给容器分配内存的工具,每个容器都需要指定一个空间配置器,用于分配内存和释放内存
我们先看一眼vector容器的源码
template <class T, class Alloc = alloc>
class vector {
typedef simple_alloc<value_type, Alloc> data_allocator;
...
};
其中的Alloc
就是空间配置器,缺省情况下指定的空间分配器为alloc
,alloc
是STL已经实现好的一个空间配置器
simple_alloc
对其进行简单的封装,让这个空间设配器变得更加好用
vector容器中在需要内存的时候,会通过以下方法获取内存
data_allocator::allocate(len);
由上述可以知道,data_allocator
是simple_alloc
,上面说simple_alloc
是对空间配置器的简单封装,我们再来看一看simple_alloc
的定义
template<class T, class Alloc>
class simple_alloc {
public:
static T *allocate(size_t n)
{ return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }
...
};
simple_alloc
其实就是调用我们指定的空间配置器的静态分配函数allocate
来分配内存
当vector容器需要释放内存的时候,会通过以下方法
data_allocator::deallocate(p, n);
我们可以知道其定义在simple_alloc
中
template<class T, class Alloc>
class simple_alloc {
public:
static void deallocate(T *p, size_t n)
{ if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }
};
从上述我们可以看到,使用simple_alloc
释放内存的时候,就是调用我们指定的空间配置器的静态释放函数deallocate
来释放内存
到这里我们就清楚空间配置器的作用了
空间配置器就是提供两个静态方法来分配和释放内存,通过allocate
分配内存,通过deallocate
释放内存
至于空间配置器内部是如何实现的,那么就需要查看源码了
二、空间配置器定义
在查看空间配置器的源码前,我们需要先找到其定义,在我提供的这份STL源码中,在stl_alloc.h
文件中,有如下的代码定义
# ifdef __USE_MALLOC
typedef malloc_alloc alloc;
...
# else
typedef default_alloc_template<NODE_ALLOCATOR_THREADS, 0> alloc;
...
#endif
可以看到alloc
要么是malloc_alloc
要么是default_alloc_template
,至于是哪一种,由宏定义__USE_MALLOC
决定
其中malloc_alloc
较为简单,几乎没有内存管理,我们称它为第一级空间配置器
default_alloc_template
较为复杂,但对内存管理非常精细,我们称它为第二级空间配置器
本文我们只讨论第一级空间配置器,第二级空间配置器将在下一篇文章中讨论
三、第一级空间配置器源码分析
接下来查看malloc_alloc
的定义(这里我忽略了很多,只保留最重要的部分)
typedef __malloc_alloc_template<0> malloc_alloc;
class __malloc_alloc_template {
...
static void (* __malloc_alloc_oom_handler)(); //malloc失败时会调用此函数处理
public:
/* 分配内存 */
static void * allocate(size_t n)
{
void *result = malloc(n);
if (0 == result) result = oom_malloc(n);
return result;
}
/* 释放内存 */
static void deallocate(void *p, size_t /* n */)
{
free(p);
}
/* 设置内存分配失败时的处理函数 */
static void (* set_malloc_handler(void (*f)()))()
{
void (* old)() = __malloc_alloc_oom_handler;
__malloc_alloc_oom_handler = f;
return(old);
}
};
我们可以看到allocate
通过malloc
分配内存,deallocate
通过free
释放内存
至于deallocate
过于简单,没有什么可谈的,下面来看一看allocate
class __malloc_alloc_template {
...
public:
static void * allocate(size_t n)
{
void *result = malloc(n);
if (0 == result) result = oom_malloc(n);
return result;
}
...
};
可以看到allocate
通过malloc
分配内存,如果分配失败就会调用oom_malloc
,我们重点查看oom_malloc
是如何处理内存分配失败的
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
{
void (* my_malloc_handler)();
void *result;
for (;;) {
my_malloc_handler = __malloc_alloc_oom_handler;
if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } //如果没有指定处理函数,那么就退出
(*my_malloc_handler)(); //调用处理函数
result = malloc(n); //分配内存
if (result) return(result); //分配成功就返回
}
}
可以看到oom_malloc
就是循环的地调用__malloc_alloc_oom_handler
函数处理,渴望__malloc_alloc_oom_handler
能够释放掉一些内存,使得malloc
能够分配到内存,如果malloc分配到内存,那么就返回
那么__malloc_alloc_oom_handler
又是那个函数呢?
你回过头看一下__malloc_alloc_template
的定义,__malloc_alloc_oom_handler
是一个静态的函数指针,用户可以通过set_malloc_handler
函数来设置它
其实上面这个就是仿造了C++ set_newhandler
机制的一个过程
好了,对于第一级空间配置器就分析到这里了,确实非常简单,下一篇文章再来分析较为复杂的第二级空间配置器