STL空間配置器剖析
阿新 • • 發佈:2019-01-22
根據情況來判定,如果配置區塊大於128bytes,說明“足夠大”,呼叫第一級配置器,而小於等於128bytes,則採用複雜記憶體池(memory pool)來管理。
obj * volatile * my_free_list這個宣告原來的形式是:obj** my_free_list,這樣的話*my_free_list(空閒的記憶體塊指標陣列中的一個元素)可能被優化到暫存器中,從而使庫程式碼無法lock住對它的讀呼叫(如果在暫存器中則另一個執行緒可能會無意中修改該暫存器的值,而在記憶體中由於另一個執行緒沒有訪問權力所以不能修改)。 要宣告變數必須在記憶體中就要用volatile修飾,這裡修飾的是*my_free_list,是free_list陣列中的一個元素,而不是陣列指標,所以volatile放在兩個*中間。template <bool threads, int inst> class __default_alloc_template { private: // 實際上我們應該使用 static const int x = N // 來取代 enum { x = N }, 但目前支援該性質的編譯器還不多。 # ifndef __SUNPRO_CC enum {__ALIGN = 8}; enum {__MAX_BYTES = 128}; enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; # endif //注意此處的位運算技巧,將bytes上調至8的倍數 static size_t ROUND_UP(size_t bytes) { return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1)); } //注意此處的union技巧 union obj { union obj* free_list_link; char client_data[1]; /* The client sees this. */ }; private: # ifdef __SUNPRO_CC static obj * __VOLATILE free_list[]; // Specifying a size results in duplicate def for 4.1 # else static obj * __VOLATILE free_list[__NFREELISTS]; # endif static size_t FREELIST_INDEX(size_t bytes) { return (((bytes) + __ALIGN - 1) / __ALIGN - 1); } // 傳回一個大小為n的物件,並可能加入大小為n的其他區塊到free list static void *refill(size_t n); // 配置一大塊空間,可容納nobjs個大小為“size”的區塊 // 如果配置nobjs個區塊有所不便,nobjs可能會降低 static char *chunk_alloc(size_t size, int &nobjs); // Chunk allocation state. static char *start_free; // 記憶池起始位置,只在chunk_alloc()中變化 static char *end_free; // 記憶池結束位置,只在chunk_alloc()中變化 static size_t heap_size; // 判斷區塊大小,大於128bytes就呼叫第一級配置器,小於128bytes就檢查相應的free list。 // 如果free list之內有可用的區塊,就直接拿來用。如果沒有可用的區塊,就將區塊大小上調至8的倍數,然後呼叫refill /* n must be > 0 */ static void * allocate(size_t n) { obj *volatile *my_free_list; obj *result; // 大於128bytes就呼叫第一級配置器 if (n > (size_t)__MAX_BYTES) { return (malloc_alloc::allocate(n)); } // 否則,在free_list尋找合適的一個 my_free_list = free_list + FREELIST_INDEX(n); result = *my_free_list; if (NULL == result) { void *r = refill(ROUND_UP(n));// 如果沒有合適的,就將n上調至8的倍數 return r; } // 調整free list *my_free_list = result->free_list_link; return result; } // n大於128bytes就呼叫第一級配置器,小於128bytes就找出對應的free list將區塊回收 /* p may not be 0 */ static void deallocate(void *p, size_t n) { obj *q = (obj*)p;; obj * volatile * my_free_list; // 大於128bytes就呼叫第一級配置器 if (n > (size_t)__MAX_BYTES) { malloc_alloc::deallocate(p,n); return; } // 否則,尋找對應的free list my_free_list = free_list + FREELIST_INDEX(n); // 調整free list,回收區塊 q->free_list_link = *my_free_list; *my_free_list = q; } static void * reallocate(void *p, size_t old_sz, size_t new_sz); // 以下是靜態資料成員的定義和初始化 template <bool threads, int inst> char *__default_alloc_template<threads, inst>::start_free = 0;//記憶體池起始位置 template <bool threads, int inst> char *__default_alloc_template<threads, inst>::end_free = 0;//記憶體池結束位置 template <bool threads, int inst> size_t __default_alloc_template<threads, inst>::heap_size = 0; template <bool threads, int inst> __default_alloc_template<threads, inst>::obj * __VOLATILE __default_alloc_template<threads, inst> ::free_list[ # ifdef __SUNPRO_CC __NFREELISTS # else __default_alloc_template<threads, inst>::__NFREELISTS # endif ] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; }; // 幾個呼叫的全域函式 // refill(),傳回一個大小為n的物件,並且有時候會為適當的free list增加節點 // 假設n已經適當上調至8的倍數 template <bool threads,int inst> void * __default_alloc_template<threads,inst>::refill(size_t n) { int nobjs = 20; // 呼叫chunk_alloc(),嘗試獲得nobjs個區塊作為free list的新節點 char * chunk = chunk_alloc(n,nobjs); obj * volatile *my_free_list; obj * result; obj * current_obj,*next_obj; int i; // 如果只獲得一個區塊,這個區塊就撥給呼叫者用,free list無新節點 if (1 == nobjs) { return chunk; } // 否則準備調整free list,納入新節點 my_free_list = free_list + FREELIST_INDEX(n); // 以下在chunk空間建立free list result = (obj*)chunk; // 這一塊準備傳回給客端 // 以下導引free list指向新配置的空間(取自記憶池) *my_free_list = next_obj = (obj*)(chunk + n); // 以下將free list的各節點串接起來 for (i = 1; ; i++) // 從1開始,因為第0個傳回給客端 { current_obj = next_obj; next_obj = (obj*)((char*)next_obj + n); if (nobjs - 1 == i) { current_obj->free_list_link = 0; break; } else { current_obj->free_list_link = next_obj; } } return result; } // 假設size已經適當上調至8的倍數 template <bool threads,int inst> char * __default_alloc_template<threads,inst>::chunk_alloc(size_t size,int& nobjs) { char *result; size_t total_bytes = size * nobjs; size_t bytes_left = end_free - start_free; // 記憶池剩餘空間 if (bytes_left >= total_bytes) { // 記憶池剩餘空間完全滿足需求量 result = start_free; start_free += total_bytes; return result; } else if (bytes_left >= size) { // 記憶池剩餘空間不能滿足需求,但足夠提供一個或以上的區塊 nobjs = bytes_left / size; total_bytes = size * nobjs; result = start_free; start_free += total_bytes; return result; } else { // 記憶池剩餘空間連一個區塊大小都無法提供 size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4); // 以下試著讓記憶池中的殘餘零頭還有利用價值 if (bytes_left > 0) { // 首先尋找合適的free list obj* volatile* my_free_list = free_list + FREELIST_INDEX(bytes_left); // 調整free list,將記憶池中的殘餘空間編入 ((obj*)start_free)->free_list_link = *my_free_list; *my_free_list = (obj*)start_free; } // 配置heap空間 start_free = (char *)malloc(bytes_to_get); if (NULL == start_free) { // heap空間不足,malloc失敗 int i; obj* volatile* my_fress_list,*p; for (i = size;i <= __MAX_BYTES;i += __ALIGN) { my_free_list = free_list + FREELIST_INDEX(i); p = *my_free_list; if (0 != p) // free list內尚有未用區塊 { // 調整free list以釋放出未用區塊 *my_free_list = p->free_list_link; start_free = (char*)p; end_free = start_free + i; // 遞迴呼叫自己,為了修正nobjs return (chunk_alloc(size,nobjs)); // 注意,任何殘餘零頭終將會被編入適當的free list中備用 } } end_free = 0; // 呼叫第一級配置器,看看oom機制能否盡點力 start_free = (char*)malloc_alloc::allocate(bytes_to_get); } heap_size += bytes_to_get; end_free = start_free + bytes_to_get; // 遞迴呼叫自己,為了修正nobjs return (chunk_alloc(size,nobjs)); } }
來源:https://blog.csdn.net/syyon123456789/article/details/51495740