STL源碼剖析(二)第一級空間配置器
文章目錄
- 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
機制的一個過程
好了,對于第一級空間配置器就分析到這裡了,确實非常簡單,下一篇文章再來分析較為複雜的第二級空間配置器