STL源碼剖析讀書筆記5

重新填充Refill_Freelist

在allocate()分配內存發現freelist沒有可用內存時,需要調用refill函數重新填充freelist,這些新的空間將從內存池中獲得,一次分配20個節點或區塊,當然如果內存池內存不足很有可能區塊會小於20個。

//假設n已經上調至8的倍數
template<bool threads,int inst>
void* _default_alloc_template<threads,inst>::refill(size_t n){
      int nobjs = 20;
      char* chunk = chunk_alloc(n,nobjs);//nobjs傳引用
      obj* volatile *my_free_list;
      if(1==nobjs) retrun(chunk);
      my_free_list = free_list + FREELIST_INDEX(n);
      obj* result;
      obj* current_obj, * next_obj;
      result = (obj*) chunk;//第一塊給客端
      *my_free_list = next_obj = (obj*)(chunk+n);//剩下的串起來 
      for(int i=1;;i++){
          currnt_obj = next_obj;
            next_obj = (obj*)((char*)next_obj+n);
          if(nobj-1 == i){
             currnt_obj->free_list_link = 0;
             break;
          }else{
             currnt_obj->free_list_link = next_obj;
           } 
      }
      return (result);
}

內存池取空間的CHUNK_ALLOC>

chunk_alloc函數是以end_free-start_free來判斷內存池是否還有可用內存,如果有則撥款20個nobjs,如若不足20但仍有超過一個需求大小的內存塊,則把這些空間全部撥出去。這時候nobjs就變成了實際得到內存塊。如果一個都無法配置,那麼內存池將會調用malloc從heap上獲取內存,內存量爲需求量的2倍加上一個和配置次數成比例的附加量。舉個例子:chunk_alloc(32,20),這是第一次內存池沒有內存,便向heap申請32*40的內存,其中1個給客端,19個給free_list維護,剩下的留給自己。這時候chunk_alloc(64,20),內存池還有32*20/64,也就是10個可用內存塊,1個給客端,9個給相應的freelist維護。自己空空如也了,下次在申請就是2倍加附加量了。萬一山窮水盡了,heap也沒了,那麼就調用第一級配置器,雖然他也是malloc,但是他有out_of_memory機制可以釋放其他內存拿來用,失敗了大不了喊一句bad_alloc;

這裏寫圖片描述

//內存池chunk_alloc的實現,模板省略了
char* chunk_alloc(size_t size,int& nobjs){
      char* result;
      size_t total_bytes = size * nobjs;
      size_t  left_bytes = end_free - start_free;
      if(left_bytes >= total_bytes){
         //內存池完全夠用
         result = start_free;
         start_free += total_bytes;
         return (result);
      }else if(left_bytes >= size){
               nobjs = left_bytes / size ;
               result = start_free;
               start_free += nobjs * size;
               return (result);
            }else{
             //內存池一個區塊的大小都無法提供
              size_t bytes_to_get = 2 * total_bytes +                        ROUND_UP(heap_size>>4);
              if(left_bytes >0){
                 //內存池還有一點零頭,先配置給合適的free_list
                 obj* volatile* my_free_list = free_list + FREELIST_INDEX(left_bytes);
                 ((obj*) start_free) -> free_list_link = *my_free_list;
                 *my_free_list = (obj*) start_free;
              }
              //在heap上配置空間,用來補充內存池
              start_free = (char*)malloc(byte_to_get);
              if(0==start_free){
                //heap空間失敗,malloc()失敗
                int i;
                obj* volatile* my_free_list,*p;
                //我們檢視手上的東西,找到適當大小的未用內存,適當即爲區塊要足夠大。
                for(i = size;i<=_MAX_BYTES;i += _ALLIN){
                    my_free_list = free_list + FREELIST_INDEX(i);
                    p = my_free_list;
                    if(0 != p){
                      //還有貨,當然我們就拿1個區塊
                       *my_free_list = p -> free_list_link;
                       start_free = (char*)p;
                         end_free = start_free + i;
                         //內存找到了,則遞歸重新配置修正nobjs
                       return (chunk_alloc(size,nobjs));
                    }
                }
                end_free = 0;
                start_free = (char*)malloc_alloc::allocate(bytes_to_get);
              }
              heap_size += bytes_to_get;
              end_free = start_free + bytes_to_get;
              return (chunk_alloc(size,nobjs));
             }
}

內存處理的基本工具

這裏寫圖片描述

該函數會調用construct(&*(result + (i-first)),*i);如果你要實現一個容器的全區間構造函數,一般經過兩個步驟,第一個配置內存空間,第二個調用copy();

這裏寫圖片描述

該函數會調用construct(&*i,*i);

這裏寫圖片描述

這裏寫圖片描述

這裏面value_type()判斷迭代器所指是否爲POD類型。POD爲plane old data,包含標量型或C 裏的結構體類型,對於POD類型必然擁有copy/assignment/dtor,因此我有更高效的初值填寫手法。否則採用最保險的做法。

這裏寫圖片描述

發佈了56 篇原創文章 · 獲贊 5 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章