1. 程式人生 > >SGISTL原始碼閱讀一 空間配置器上(第一級配置器)

SGISTL原始碼閱讀一 空間配置器上(第一級配置器)

SGISTL原始碼閱讀一 空間配置器上(第一級配置器)

引入

我們所熟知的C++記憶體配置操作一般為

class A {}
A* pa = new A();  	//1.分配記憶體 2.構造物件
delete pa;			//1.物件析構 2.釋放記憶體

其中new完成了兩個操作
1.呼叫operator new配置記憶體
2.呼叫A::A()構造物件

這樣做沒有考慮到任何效率上的強化,如果有些空間根本不會被用到,那麼構造物件完全就是一種浪費。
而STL中的alloc將new所完成的操作拆分開來,SGISTL原始碼中有兩個檔案,stl_alloc.h負責記憶體空間的配置與釋放,stl_construct.h

負責物件內容的構造與析構。

SGI特殊的空間配置器

物件構造前的空間配置和物件析構後的空間釋放,由stl_alloc.h負責。
考慮到小型區塊可能造成的記憶體破碎問題,SGI設計了雙層級配置器。
第一級配置器用於處理大於128bytes的情況,即申請大的記憶體,第二級配置器用於申請小記憶體。

第一級配置器(__malloc_alloc_template)

直接使用malloc()free()即可,我們來深入原始碼探究。

//這裡用到了模板全特化技術
template <int inst>
class __malloc_alloc_template {
    private:
    /*
     *oom(out of memory)
     *下面的函式用於處理記憶體不足的情況
     */
    static void *oom_malloc(size_t);

    static void *oom_realloc(void *, size_t);

    #ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
    //函式指標,指向引數和返回值均為空的函式
        static void (* __malloc_alloc_oom_handler)();
    #endif

    public:
    //申請n個大小的空間
    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 * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
    {
        void * result = realloc(p, new_sz);
        if (0 == result) result = oom_realloc(p, new_sz);
        return result;
    }
    /*
     *這是一個返回值為函式指標的指標函式(傳送門:https://blog.csdn.net/lyn_00/article/details/83549655)
     *模擬C++的set_new_handler()
     *它的作用是可以讓使用者自己定義記憶體不足時的處理函式,並返回舊的處理方法
     */
    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>
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
#endif

template <int inst>
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);
    }
}

//realloc和malloc類似
template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, 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 = realloc(p, n);
        if (result) return(result);
    }
}
//直接將引數inst指定為0
typedef __malloc_alloc_template<0> malloc_alloc;

總結

以上主要對SGI中空間配置器的第一級配置器進行了分析。它如何申請釋放記憶體,以及對記憶體不足時的處理。第二級配置器比第一級配置器要複雜得多,將會在下一篇部落格中介紹。