天天看點

STL源碼剖析(二)第一級空間配置器STL源碼剖析(二)第一級空間配置器

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

機制的一個過程

好了,對于第一級空間配置器就分析到這裡了,确實非常簡單,下一篇文章再來分析較為複雜的第二級空間配置器

繼續閱讀