日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

STL中的空间配置器

發(fā)布時間:2024/4/11 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STL中的空间配置器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

STL中的空間配置器

文章目錄

  • STL中的空間配置器
    • 1. 什么是空間配置器
    • 2. 為什么需要空間配置器
    • 3. SGI-STL空間配置器實現(xiàn)原理
      • 3.1 一級空間配置器
      • 3.2 二級空間配置器
        • 3.1 內(nèi)存池
        • 3.2 SGI-STL中二級空間配置器設(shè)計
        • 3.3 SGI-STL二級空間配置器之空間申請
      • 3.3 空間配置器的默認選擇
      • 3.4 空間配置器的再次封裝
      • 3.5 對象的構(gòu)造與釋放
    • 4. 與容器結(jié)合
    • 5.總結(jié)

1. 什么是空間配置器

空間配置器,顧名思義就是為各個容器高效的管理空間(空間的申請與回收)的,在默默地工作

2. 為什么需要空間配置器

在模擬實現(xiàn)vector、list、map、unordered_map等容器時,所有需要空間的地方都是通過new申請 的,雖然代碼可以正常運行,但是有以下不足之處:

  • 空間申請與釋放需要用戶自己管理,容易造成內(nèi)存泄漏
  • 頻繁向系統(tǒng)申請小塊內(nèi)存塊,容易造成內(nèi)存碎片
  • 頻繁向系統(tǒng)申請小塊內(nèi)存,影響程序運行效率
  • 直接使用malloc與new進行申請,每塊空間前有額外空間浪費
  • 申請空間失敗怎么應(yīng)對
  • 代碼結(jié)構(gòu)比較混亂,代碼復用率不高
  • 未考慮線程安全問題

因此需要設(shè)計一塊高效的內(nèi)存管理機制。

3. SGI-STL空間配置器實現(xiàn)原理

  • 以上提到的幾點不足之處,最主要還是:頻繁向系統(tǒng)申請小塊內(nèi)存造成的
  • 那什么才算是小塊內(nèi)存?SGI-STL 以128作為小塊內(nèi)存與大塊內(nèi)存的分界線
  • 將空間配置器其分為兩級結(jié)構(gòu),一級空間配置器處理大塊內(nèi)存, 二級空間配置器處理小塊內(nèi)存。

3.1 一級空間配置器

一級空間配置器主要針對大于128字節(jié)的空間,原理非常簡單,直接對malloc與free進行了封裝,并增加了C++中set_new_handle思想

template <int inst> class __malloc_alloc_template { private:static void *oom_malloc(size_t);public:// 對malloc的封裝static void * allocate(size_t n){// 申請空間成功,直接返回,失敗交由oom_malloc處理void *result = malloc(n);if (0 == result)result = oom_malloc(n);return result;}// 對free的封裝static void deallocate(void *p, size_t /* n */){ free(p);}// 模擬set_new_handle// 該函數(shù)的參數(shù)為函數(shù)指針,返回值類型也為函數(shù)指針// void (* set_malloc_handler( void (*f)() ) )()static void (* set_malloc_handler(void (*f)()))(){void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return(old);} };// malloc申請空間失敗時代用該函數(shù)template <int inst>void * __malloc_alloc_template<inst>::oom_malloc(size_t n){void (* my_malloc_handler)();void *result;for (;;){// 檢測用戶是否設(shè)置空間不足應(yīng)對措施,如果沒有設(shè)置,拋異常,模式new的方式my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler){__THROW_BAD_ALLOC;}// 如果設(shè)置,執(zhí)行用戶提供的空間不足應(yīng)對措施(*my_malloc_handler)();// 繼續(xù)申請空間,可能就會申請成功 result = malloc(n);if (result)return(result);}} typedef __malloc_alloc_template<0> malloc_alloc;

3.2 二級空間配置器

二級空間配置器專門負責處理小于128字節(jié)的小塊內(nèi)存。如何才能提升小塊內(nèi)存的申請與釋放的方式呢?SGI- STL采用了內(nèi)存池的技術(shù)來提高申請空間的速度以及減少額外空間的浪費,采用哈希桶的方式來提高用戶獲 取空間的速度與高效管理。

3.1 內(nèi)存池

  • 內(nèi)存池就是:先申請一塊比較大的內(nèi)存塊已做備用,當需要內(nèi)存時,直接到內(nèi)存池中去去,當池中空間不夠時,再向內(nèi)存中去取,當用戶不用時,直接還回內(nèi)存池即可。
  • 避免了頻繁向系統(tǒng)申請小塊內(nèi)存所造成的效率低、內(nèi)存碎片以及額外浪費的問題

3.2 SGI-STL中二級空間配置器設(shè)計

  • SGI-STL中的二級空間配置器使用了內(nèi)存池技術(shù),但沒有采用鏈表的方式對用戶已經(jīng)歸還的空間進行管理(因為用戶申請空間時在查找合適的小塊內(nèi)存時效率比較低)
  • 而是采用了哈希桶的方式進行管理
  • 那是否需要 128桶個空間來管理用戶已經(jīng)歸還的內(nèi)存塊呢?答案是不需要,因為用戶申請的空間基本都是4的整數(shù)倍,其 他大小的空間幾乎很少用到。因此:SGI-STL將用戶申請的內(nèi)存塊向上對齊到了8的整數(shù)倍

3.3 SGI-STL二級空間配置器之空間申請

1. 前期的準備

// 去掉代碼中繁瑣的部分 template <int inst> class __default_alloc_template { private:enum {__ALIGN = 8}; // 如果用戶所需內(nèi)存不是8的整數(shù)倍,向上對齊到8的整數(shù)倍enum {__MAX_BYTES = 128}; // 大小內(nèi)存塊的分界線enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; // 采用哈希桶保存小塊內(nèi)存時所需桶的個數(shù)// 如果用戶所需內(nèi)存塊不是8的整數(shù)倍,向上對齊到8的整數(shù)倍static size_t ROUND_UP(size_t bytes){return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1));} private:// 用聯(lián)合體來維護鏈表結(jié)構(gòu)union obj{union obj * free_list_link;char client_data[1]; /* The client sees this. */}; private:static obj * free_list[__NFREELISTS];// 哈希函數(shù),根據(jù)用戶提供字節(jié)數(shù)找到對應(yīng)的桶號static size_t FREELIST_INDEX(size_t bytes){return (((bytes) + __ALIGN-1)/__ALIGN - 1);}// start_free與end_free用來標記內(nèi)存池中大塊內(nèi)存的起始與末尾位置static char *start_free;static char *end_free;// 用來記錄該空間配置器已經(jīng)想系統(tǒng)索要了多少的內(nèi)存塊static size_t heap_size;// ... } ;

2. 申請空間

// 函數(shù)功能:向空間配置器索要空間 // 參數(shù)n: 用戶所需空間字節(jié)數(shù) // 返回值:返回空間的首地址 static void * allocate(size_t n) {obj * __VOLATILE * my_free_list;obj * __RESTRICT result;// 檢測用戶所需空間釋放超過128(即是否為小塊內(nèi)存)if (n > (size_t) __MAX_BYTES){// 不是小塊內(nèi)存交由一級空間配置器處理return (malloc_alloc::allocate(n));}// 根據(jù)用戶所需字節(jié)找到對應(yīng)的桶號my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;// 如果該桶中沒有內(nèi)存塊時,向該桶中補充空間if (result == 0){// 將n向上對齊到8的整數(shù)被,保證向桶中補充內(nèi)存塊時,內(nèi)存塊一定是8的整數(shù)倍void *r = refill(ROUND_UP(n));return r;}// 維護桶中剩余內(nèi)存塊的鏈式關(guān)系*my_free_list = result -> free_list_link;return (result); };

3. 填充內(nèi)存塊

// 函數(shù)功能:向哈希桶中補充空間 // 參數(shù)n:小塊內(nèi)存字節(jié)數(shù) // 返回值:首個小塊內(nèi)存的首地址 template <int inst> void* __default_alloc_template<inst>::refill(size_t n) {// 一次性向內(nèi)存池索要20個n字節(jié)的小塊內(nèi)存int nobjs = 20;char * chunk = chunk_alloc(n, nobjs);obj ** my_free_list;obj *result;obj *current_obj, *next_obj;int i;// 如果只要了一塊,直接返回給用戶使用if (1 == nobjs)return(chunk);// 找到對應(yīng)的桶號my_free_list = free_list + FREELIST_INDEX(n);// 將第一塊返回值用戶,其他塊連接在對應(yīng)的桶中// 注:此處代碼邏輯比較簡單,但標準庫實現(xiàn)稍微有點復雜,同學們可以自己實現(xiàn)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 -> free_list_link = 0;break;}else{current_obj -> free_list_link = next_obj;}}return(result); }

4. 向內(nèi)存池中索要空間

template <int inst> char* __default_alloc_template<inst>::chunk_alloc(size_t size, int& nobjs) {// 計算nobjs個size字節(jié)內(nèi)存塊的總大小以及內(nèi)存池中剩余空間總大小char * result;size_t total_bytes = size * nobjs;size_t bytes_left = end_free - start_free;// 如果內(nèi)存池可以提供total_bytes字節(jié),返回if (bytes_left >= total_bytes){result = start_free;start_free += total_bytes;return(result);}else if (bytes_left >= size){// nobjs塊無法提供,但是至少可以提供1塊size字節(jié)內(nèi)存塊,提供后返回nobjs = bytes_left/size;total_bytes = size * nobjs;result = start_free;start_free += total_bytes;return(result);}else{// 內(nèi)存池空間不足,連一塊小塊村內(nèi)都不能提供// 向系統(tǒng)堆求助,往內(nèi)存池中補充空間// 計算向內(nèi)存中補充空間大小:本次空間總大小兩倍 + 向系統(tǒng)申請總大小/16size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);// 如果內(nèi)存池有剩余空間(該空間一定是8的整數(shù)倍),將該空間掛到對應(yīng)哈希桶中if (bytes_left > 0){// 找對用哈希桶,將剩余空間掛在其上obj ** my_free_list = free_list + FREELIST_INDEX(bytes_left);((obj *)start_free) -> free_list_link = *my_free_list;*my_ree_list = (obj *)start_free;}// 通過系統(tǒng)堆向內(nèi)存池補充空間,如果補充成功,遞歸繼續(xù)分配start_free = (char *)malloc(bytes_to_get);if (0 == start_free){// 通過系統(tǒng)堆補充空間失敗,在哈希桶中找是否有沒有使用的較大的內(nèi)存塊int i;obj ** my_free_list, *p;for (i = size; i <= __MAX_BYTES; i += __ALIGN){my_free_list = free_list + FREELIST_INDEX(i);p = *my_free_list;// 如果有,將該內(nèi)存塊補充進內(nèi)存池,遞歸繼續(xù)分配if (0 != p){*my_free_list = p -> free_list_link;start_free = (char *)p;end_free = start_free + i;return(chunk_alloc(size, nobjs));}}// 山窮水盡,只能向一級空間配置器求助// 注意:此處一定要將end_free置空,因為一級空間配置器一旦拋異常就會出問題end_free = 0;start_free = (char *)malloc_alloc::allocate(bytes_to_get);}// 通過系統(tǒng)堆向內(nèi)存池補充空間成功,更新信息并繼續(xù)分配heap_size += bytes_to_get;end_free = start_free + bytes_to_get;return(chunk_alloc(size, nobjs));} }

3.4 SGI-STL二級空間配置器之空間回收

// 函數(shù)功能:用戶將空間歸還給空間配置器 // 參數(shù):p空間首地址 n空間總大小 static void deallocate(void *p, size_t n) {obj *q = (obj *)p;obj ** my_free_list;// 如果空間不是小塊內(nèi)存,交給一級空間配置器回收if (n > (size_t) __MAX_BYTES){malloc_alloc::deallocate(p, n);return;}// 找到對應(yīng)的哈希桶,將內(nèi)存掛在哈希桶中my_free_list = free_list + FREELIST_INDEX(n);q -> free_list_link = *my_free_list;*my_free_list = q; }

3.3 空間配置器的默認選擇

SGI-STL默認使用一級還是二級空間配置器,通過USE_MALLOC宏進行控制:

#ifdef __USE_MALLOCtypedef malloc_alloc alloc;typedef malloc_alloc single_client_alloc; #else// 二級空間配置器定義 #endif

在SGI_STL中該宏沒有定義,因此:默認情況下SGI_STL使用二級空間配置器

3.4 空間配置器的再次封裝

在C++中,用戶所需空間可能是任意類型的,有單個對象空間,有連續(xù)空間,每次讓用戶自己計算所需空間總大小不是很友好,因此SGI-STL將空間配置器重新再封裝了一層

// T: 元素類型 // Alloc: 空間配置器 // 注意:該類只負責申請與歸還對象的空間,不否則空間中對象的構(gòu)造與析構(gòu) template<class T, class Alloc> class simple_alloc { public:// 申請n個T類型對象大小的空間static T *allocate(size_t n){return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T));}// 申請一個T類型對象大小的空間static T *allocate(void){return (T*) Alloc::allocate(sizeof (T));}// 釋放n個T類型對象大小的空間static void deallocate(T *p, size_t n){if (0 != n)Alloc::deallocate(p, n * sizeof (T));}// 釋放一個T類型對象大小的空間static void deallocate(T *p){Alloc::deallocate(p, sizeof (T));} };

3.5 對象的構(gòu)造與釋放

一切為了效率考慮,SGI-STL決定將空間申請釋放和對象的構(gòu)造析構(gòu)兩個過程分離開,因為有些對象的構(gòu)造不需要調(diào)用析構(gòu)函數(shù),銷毀時不需要調(diào)用析構(gòu)函數(shù),將該過程分離開可以提高程序的性能:

// 歸還空間時,先先調(diào)用該函數(shù)將對象中資源清理掉 template <class T> inline void destroy(T* pointer) {pointer->~T(); } // 空間申請好后調(diào)用該函數(shù):利用placement-new完成對象的構(gòu)造 template <class T1, class T2> inline void construct(T1* p, const T2& value) {new (p) T1(value); }

注意:
1. 在釋放對象時,需要根據(jù)對象的類型確定是否調(diào)用析構(gòu)函數(shù)(類型萃取)
2. 對象的類型可以通過迭代器獲萃取到

4. 與容器結(jié)合

本例子給出list與空間配置器是如何結(jié)合的

template <class T, class Alloc = alloc> class list {// ...// 實例化空間配置器typedef simple_alloc<list_node, Alloc> list_node_allocator;// ... protected:link_type get_node(){ // 調(diào)用空間配置器接口先申請節(jié)點的空間return list_node_allocator::allocate();}// 將節(jié)點歸還給空間配置器void put_node(link_type p){list_node_allocator::deallocate(p);}// 創(chuàng)建節(jié)點:1. 申請空間 2. 完成節(jié)點構(gòu)造link_type create_node(const T& x){link_type p = get_node();construct(&p->data, x);return p;}// 銷毀節(jié)點: 1. 調(diào)用析構(gòu)函數(shù)清理節(jié)點中資源 2. 將節(jié)點空間歸還給空間配置器void destroy_node(link_type p){destroy(&p->data);put_node(p);}// ...iterator insert(iterator position, const T& x){link_type tmp = create_node(x);tmp->next = position.node;tmp->prev = position.node->prev;(link_type(position.node->prev))->next = tmp;position.node->prev = tmp;return tmp;}iterator erase(iterator position){link_type next_node = link_type(position.node->next);link_type prev_node = link_type(position.node->prev);prev_node->next = next_node;next_node->prev = prev_node;destroy_node(position.node);return iterator(next_node);}// ... };

5.總結(jié)

  • new運算分兩個階段:

(1)調(diào)用::operator new配置內(nèi)存;
(2)調(diào)用對象構(gòu)造函數(shù)構(gòu)造對象內(nèi)容

  • delete運算分兩個階段:

(1)調(diào)用對象希構(gòu)函數(shù);
(2)調(diào)用operator delete釋放內(nèi)存

  • 為了精密分工,STL allocator將兩個階段操作區(qū)分開來:
  • 內(nèi)存配置有alloc::allocate()負責,內(nèi)存釋放由alloc::deallocate()負責;
  • 對象構(gòu)造由::construct()負責,對象析構(gòu)由::destroy()負責。

同時為了提升內(nèi)存管理的效率,減少申請小內(nèi)存造成的內(nèi)存碎片問題。

  • SGI STL采用了兩級配置器,當分配的空間大小超過128字節(jié)時,會使用第一級空間配置器;
  • 當分配的空間大小小于128字節(jié)時,將使用第二級空間配置器。
  • 第一級空間配置器直接使用malloc()、realloc()、free()函數(shù)進行內(nèi)存空間的分配和釋放
  • 而第二級空間配置器采用了內(nèi)存池技術(shù),通過空閑鏈表來管理內(nèi)存。

總結(jié)

以上是生活随笔為你收集整理的STL中的空间配置器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。