STL二級空間配置器

STL的二級空間配置器相似於memory_pool,可是並非memory_pool,由於STL的二級空間配置器只維護了16個free_list,並且只是小於128byte大小的小區塊,大於128byte會調用一級空間配置器malloc。16個鏈表放在數組中,每個數組所鏈的鏈表表明大小相等的小區塊,由於分配的時候,只會申請8的倍數大小的空間,因此小於128byte的區塊確定是屬於free_list裏面。當小區塊釋放時,就會掛到free_list中。數組

下面是二級空間配置器的源碼:安全

template <bool threads, int inst>class __default_alloc_template

_ALIGN = 8,表示最小的小區塊的大小
_MAX_BYTES = 128 表示最大的區塊的大小
_NFREELISTS = 16 表示free_list的個數
#if ! (defined(__SUNPRO_CC) || defined(__GNUC__))
    enum {_ALIGN = 8};
    enum {_MAX_BYTES = 128};
    enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN
# endif
這是將申請的大小按8字節對齊:
static size_t
  _S_round_up(size_t __bytes) 
    { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }

這是free_list的Node結構體:多線程

__PRIVATE:
  union _Obj {
        union _Obj* _M_free_list_link;
        char _M_client_data[1];    /* The client sees this.        */
  };

free_list的定義:函數

# if defined(__SUNPRO_CC) || defined(__GNUC__) || defined(__HP_aCC)
    static _Obj* __STL_VOLATILE _S_free_list[]; 
        // Specifying a size results in duplicate def for 4.1
# else
    static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];  //volatile 是爲了防止在多線程下產生不可預期的後果
# endif

內存池的相關信息:ui

static char* _S_start_free; //內存池中未分給free_pool的首地址
 static char* _S_end_free; //內存池的末尾地址
 static size_t _S_heap_size;//內存池的總空間大小

二級空間配置器分配函數:this

static void* allocate(size_t __n)
  {
    void* __ret = 0;

    //若是size大於128byte就使用一級配置器
    if (__n > (size_t) _MAX_BYTES) {
      __ret = malloc_alloc::allocate(__n);
    }
    else {
      _Obj* __STL_VOLATILE* __my_free_list
          = _S_free_list + _S_freelist_index(__n); //找到size對應的list
      // Acquire the lock here with a constructor call.
      // This ensures that it is released in exit or during stack
      // unwinding.
#     ifndef _NOTHREADS
      /*REFERENCED*/
      _Lock __lock_instance;//有關線程安全
#     endif
      _Obj* __RESTRICT __result = *__my_free_list; //指向size所在鏈表頭 
      if (__result == 0)
        __ret = _S_refill(_S_round_up(__n));  //當這個鏈表爲空時,從新建立一個小chunk
      else {
        *__my_free_list = __result -> _M_free_list_link; //將鏈第一個節點取下來
        __ret = __result; //給用戶返回取下來的小區塊的地址
      }
    }
    return __ret;
 };

下面的函數是從一級空間配置器(malloc)申請小塊內存的函數:線程

template <bool __threads, int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
    int __nobjs = 20;
    /*默認的建議值,在申請小內存塊的時候,一次申請的個數,這並非最終個數,會根據內存池剩下的空間大小進行調整*/
    char* __chunk = _S_chunk_alloc(__n, __nobjs);//從內存池中申請_nobjs個_n大小的空間
    _Obj* __STL_VOLATILE* __my_free_list; //指向free_list的指針
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    /*若是在_S_chunk_alloc中只申請到了一塊這樣大的空間,則直接返回*/
    if (1 == __nobjs) return(__chunk);
    __my_free_list = _S_free_list + _S_freelist_index(__n); //找到該大小所對應的free_list

    /*將獲得的多塊_n大小的chunk,第一塊返回給用戶,剩下的加到_n所對應的free_list中*/
      __result = (_Obj*)__chunk;
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
      for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return(__result);
}

_S_chunk_alloc:指針

template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size,                                    int& __nobjs)
{
    char* __result;
    size_t __total_bytes = __size * __nobjs; //想要開闢的總大小
    size_t __bytes_left = _S_end_free - _S_start_free; //內存池的剩餘大小

/*判斷內存池剩餘的空間和須要申請的空間的大小比較*/
    if (__bytes_left >= __total_bytes) {
        __result = _S_start_free; //大於就能夠直接分配
        _S_start_free += __total_bytes;
        return(__result);
    } else if (__bytes_left >= __size) {
        __nobjs = (int)(__bytes_left/__size);//調整須要申請的塊數 //使用內存池剩餘空間分配大小爲_size的內存塊,能分多少塊就分多少塊
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;  //將內存池的start往下移,分配空間
        return(__result);
    } else {
        size_t __bytes_to_get = 
      2 * __total_bytes + _S_round_up(_S_heap_size >> 4);  //內存吃沒有足夠的空間分配時,須要從新申請空間,這裏的size就是須要向一級空間配置器申請的空間大小
        if (__bytes_left > 0) {
            _Obj* __STL_VOLATILE* __my_free_list =
                        _S_free_list + _S_freelist_index(__bytes_left);//將內存池剩餘的小於_size的空間掛到對應大小的free_list上。

            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;
        }
        /*內存池剩餘爲0時,malloc一個小塊內存,進行分配*/
        _S_start_free = (char*)malloc(__bytes_to_get);
        if (0 == _S_start_free) {
            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
        _Obj* __p;
        /*若是這是第一次申請內存池空間,則進行循環分配小塊空間*/
            for (__i = __size;
                 __i <= (size_t) _MAX_BYTES;
                 __i += (size_t) _ALIGN) {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
                if (0 != __p) {
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;
                    return(_S_chunk_alloc(__size, __nobjs));

                }
            }
        //將申請的空間分配完以後,再申請一塊內存池空間做爲備用
        _S_end_free = 0; 
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);

        }
        /*若是不是第一次申請內存池空間,把內存池的大小擴大,再調用本身進行空間分配*/
        _S_heap_size += __bytes_to_get;
        _S_end_free = _S_start_free + __bytes_to_get;
        return(_S_chunk_alloc(__size, __nobjs));
    }
}

這就是整個STL庫中的二級空間配置器的流程,其實也在模仿ptmalloc的內存管理機制,使用內存池和空間回收,避免屢次庫函數調用或者系統調用,大大節省了程序運行的時間。這裏的二級空間配置器只要用到了free_list和小塊內存池,雙層保證小塊空間分配的速率比直接malloc快。可是在實戰中的效率對比仍是須要實踐才能得出結論。code

相關文章
相關標籤/搜索