<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          std 源碼剖析及 C++ 內存管理(二)

          共 17180字,需瀏覽 35分鐘

           ·

          2021-09-03 18:17

          ??


          大家好,我是唐唐!

          本文關于 C++ 內存管理學習筆記自侯捷,上次筆記見 C++ 內存管理(一)。

          1.各個標準分配器實現(xiàn)

          1.1 VC6.0 malloc

          在第一節(jié)中提到,malloc 的內存塊布局如上,其中 cookie (記錄區(qū)塊大小)小,浪費率高,因為 cookie 始終占 8 字節(jié)。cookie 是我們不需要的,如果大量調用 malloc 的話 cookie 總和會增多,這會造成較大的浪費,因此減少 malloc 調用次數(shù),去掉 cookie 總是好的。

          1.2 VC6.0標準分配器

          結論:VC6.0 的 allocate() 函數(shù)只是對 malloc 的二次封裝,并沒有做什么很特殊的操作,它是以類型字節(jié)長度為單位分配內存的,上圖就分配了512個int類型空間。
          結論:同 vc6.0。

          1.3 G2.9 malloc

          GCC 2.9 版本的 allocator 如上圖所示,同樣這里的 allocator 同前面提到的幾個標準分配器一樣。
          而 g2.9 容器使用的分配器,不是 std::allocator,而是std::alloc。
          對于前面提到的 malloc 設計,如果想要優(yōu)化,可以減少malloc次數(shù),同時減少cookie。而去除 cookie 的先決條件是你的 cookie 大小一致。容器里面的元素是一樣的大小,這就滿足了先決條件!
          分配器的客戶不是給你應用程序用,而是給容器用。

          1.4 G4.9 malloc

          在 GCC 4.9 版本,2.9 版本的 alloc 變成了 __pool_alloc。
          從上面兩張圖可以對比看出,2.9 版本的 allocate 和 4.9 版本的 __pool_alloc 做的事是一樣的,只是修改了變量名和一些細小操作而已。

          1.5 G4.9結構

          g4.9 的 __pool_alloc 是我們在容器中使用的分配器。而普通的 allocator,則是通過 operator new 與 operator delete 調用 malloca 與 free。其實沒有什么特殊設計。
          最后,來個測試用例,通過對比 allocator 與 __pool_alloc 來看看連續(xù)地址相差字節(jié),來判斷是否攜帶 cookie。
          #include <iostream>#include <vector>#include <ext/pool_allocator.h>
          using namespace std;
          template<typename Alloc>void cookie_test(Alloc alloc, size_t n){ typename Alloc::value_type *p1, *p2, *p3;//需有 typename p1 = alloc.allocate(n); //allocate() and deallocate() 是 non-static, 需以 object 呼叫之. p2 = alloc.allocate(n); p3 = alloc.allocate(n);
          cout << "p1= " << p1 << '\t' << "p2= " << p2 << '\t' << "p3= " << p3 << '\n';
          alloc.deallocate(p1,sizeof(typename Alloc::value_type)); //需有 typename alloc.deallocate(p2,sizeof(typename Alloc::value_type)); //有些 allocator 對於 2nd argument 的值無所謂 alloc.deallocate(p3,sizeof(typename Alloc::value_type));}
          int main(void){ cout << sizeof(__gnu_cxx::__pool_alloc<double>) << endl; vector<int, __gnu_cxx::__pool_alloc<double> > vecPool; cookie_test(__gnu_cxx::__pool_alloc<double>(), 1);
          cout << "----------------------" << endl;
          cout << sizeof(std::allocator<double>) << endl; vector<int, std::allocator<double> > vecPool2; cookie_test(std::allocator<double>(), 1);
          return 0;}
          輸出:
          1p1= 0x5557fc0f1280p2= 0x5557fc0f1288p3= 0x5557fc0f1290----------------------1p1= 0x5557fc0f13d0p2= 0x5557fc0f13f0p3= 0x5557fc0f1410
          可以發(fā)現(xiàn)容器使用的 __pool_alloc 后,連續(xù)地址相差 8 字節(jié),而一個 double 類型變量的大小也是 8 個字節(jié),說明這連續(xù)幾塊內存之間是不帶 cookie 的(即使這幾塊內存在物理上也是不連續(xù)的)。而后面那個則相差更多(相差 32 字節(jié),攜帶了 cookie )。

          2.std::alloc

          2.1 G2.9 運作模式

          G2.9 std::alloc 運作模式使用一個 16 個攜帶指針頭的數(shù)組來管理內存鏈表,而我們上一章只是用了一條鏈表。數(shù)組不同的元素管理不同的區(qū)塊,每個元素之間相差 8 字節(jié),例如 #3 號元素負責管理 32bytes 為一小塊的鏈表。
          途中 pool 就是戰(zhàn)備池( start_free 與 end_free 中間部分),所以總是把分配的東西放到戰(zhàn)備池中,再從戰(zhàn)備池挖適當?shù)目臻g到鏈表來。這樣構思,代碼寫起來特別漂亮。
          假設現(xiàn)在用戶需要 32 字節(jié)的內存,std::allloc 先申請一塊區(qū)間,為 32*20*2 大小,用一條鏈表管理,然后讓數(shù)組的#3鏈表指針管理這條鏈表。接著講該以 32 為一個單元的鏈表的中的一個單元( 32 字節(jié))分給用戶。(對應圖中綠色部分).
          為什么是 32*20*2?
          前面 32*20 空間是分配給用戶的,但是后面的 32*20 空間是預留的,如圖所示,如果這時用戶需要一個 64 字節(jié)的空間,那么剩下的 32*20 空間將變成 64*10,然后將其中 64 字節(jié)分配給用戶,而不用再一次地構建鏈表和申請空間。其中 20 是開發(fā)團隊設計的一個值。
          如果該鏈表組維護的鏈表最大的一個小塊為 128byte,但是用戶申請內存塊超過了 128byte,那么 std::alloc 將調用 malloc 給用戶分配空間,然后該塊將帶上 cookie頭和尾。
          前面一節(jié)提到內存管理的核心設計:嵌入式指針.在真正的商業(yè)級的內存分配器中,一般都會使用嵌入式指針,將每一個小塊的前四個字節(jié)用作指針連接下一塊可用的內存塊。這樣不需要多分配額外空間,就可以完成任務。

          2.2 std::alloc運行過程

          申請32bytes圖
          32 字節(jié)對應 #3 指針所指向的鏈表,此時由于戰(zhàn)備池為空,故向戰(zhàn)備池中充值 32*20*2+RoundUp(0>>4=1280) ,從中切出一塊返回給客戶,剩余 19 塊,累計申請量有 1280 字節(jié),戰(zhàn)備池有 640 字節(jié).
          RoundUp 實現(xiàn) 8 字節(jié)對齊.例如傳入 13,傳出的就是 16. 0>>4 表示右移 4 位,每次除以 16.
          申請 64bytes 圖
          上次的戰(zhàn)備池有 640 字節(jié),下次的分配就會從戰(zhàn)備池中取,這次申請 64 字節(jié),對應到#7鏈表指針,此時使用戰(zhàn)備池中的字節(jié)做區(qū)塊,可以得到 10 個,從中切出一塊返回給用戶,剩余 9,此時累計申請量:1280 ,戰(zhàn)備池大小此時為 0。
          申請 96bytes 圖
          由于戰(zhàn)備池中沒有余量,此時向戰(zhàn)備池中注入96*20*2+RoundUp(1280>>4) 其余原理同上。
          后面的申請圖同上原理。
          戰(zhàn)備池不夠了,碎片狀態(tài)如何處理:
          在前面的戰(zhàn)備池中還有 24 字節(jié),此時需要 72 字節(jié),戰(zhàn)備池中 1 個區(qū)塊都不能夠滿足,因此要先解決 24 區(qū)字節(jié)碎片,再重新往 #8 中充值。
          碎片處理,24 字節(jié)對應的是 #2,那么把剛才的 24 字節(jié)塊拉到 #2 即可。
          此時要重新往 #8 中充值,同事此時假設系統(tǒng)的 heap 大小為 10000,此時分配的72*20*2+RoundUp(9688>>4 再加上之前的累計申請量,更新后就超過了 10000,資源不夠了,那此時就需要從后面最近的鏈表元素借。在上一個圖中我們發(fā)現(xiàn) #9 滿足,此時 80 - 72=8,也就是戰(zhàn)備池為 8。切除了 72 返回給用戶。
          再申請 72 字節(jié)原理結合了碎片處理與上面的資源限制處理:
          此時申請 120 字節(jié),對應 #14,根據(jù)上述原理,此時已經(jīng)山窮水盡!
          山窮水盡了,怎么辦,此時給出了兩種辦法,但是在 GCC 中沒有采納任何作法!

          3.std::allloc 源碼剖析

          3.1 G2.9 與 G4.9 簡要對比

          侯老師的 ppt 講解源碼是 G2.9,這里我將以 G4.9 自學方式對比課上 G2.9 內容。
          在 G2.9 中有 std::alloc 的第一級分配器與第二級分配器,在 G4.9 中只有前面的第二級分配器,因此侯老師在講解過程中先從第二級分配器講解,只提及第一級分配器中的設計注意點,下面一起來學習。
          上面是 G2.9 的源碼,其中分配器為 __default_alloc_template,一開始默認使用的分配器,在該類中定義了 ROUND_UP 函數(shù),用來將申請內存數(shù)量做 8 字節(jié)對齊。
          定義了 union free_list_link,嵌入式指針,在上一章中我們構建的一個小的分配器中也定義了該聯(lián)合體,作用類似,該聯(lián)合體只有一個成員,因此可以使用 struct 代替。
          free_list 是一個有 16個obj* 元素的數(shù)組,在前面講過,GCC 2.9 的分配器用一個16 字節(jié)數(shù)組管理 16 條鏈表,free_list 便是該管理數(shù)組。
          refill 和 chunk_alloc 在后面再介紹。start_free 和 end_free 分別指向該內存池的頭和尾。中間管理的就是戰(zhàn)備池!
          這一塊對應到 G4.9 中,代碼如下:
          class __pool_alloc_base{protected:
          enum { _S_align = 8 }; enum { _S_max_bytes = 128 }; enum { _S_free_list_size = (size_t)_S_max_bytes / (size_t)_S_align };
          union _Obj{union _Obj* _M_free_list_link;char _M_client_data[1]; // The client sees this.};
          static _Obj* volatile _S_free_list[_S_free_list_size];
          // Chunk allocation state. static char* _S_start_free; static char* _S_end_free; static size_t _S_heap_size;
          size_t _M_round_up(size_t __bytes){ return ((__bytes + (size_t)_S_align - 1) & ~((size_t)_S_align - 1)); }
          _GLIBCXX_CONST _Obj* volatile* _M_get_free_list(size_t __bytes) throw ();
          // Returns an object of size __n, and optionally adds to size __n // free list. void* _M_refill(size_t __n);
          // Allocates a chunk for nobjs of size size. nobjs may be reduced // if it is inconvenient to allocate the requested number. char* _M_allocate_chunk(size_t __n, int& __nobjs);};
          這個代碼在
          https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/ext/pool_allocator.h
          中可以找到,同時可以下載  gcc4.9  版本源碼,到 include 里面查找!
          除了這一塊代碼,發(fā)現(xiàn)還有 FREELIST_INDEX 代碼沒有找到對應的,我們再繼續(xù)扒一下源碼:
          __pool_alloc_base::_M_get_free_list(size_t __bytes) throw (){ size_t __i = ((__bytes + (size_t)_S_align - 1) / (size_t)_S_align - 1); return _S_free_list + __i;}
          可在:
          https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/pool_allocator.cc
          中找到源碼實現(xiàn).對比發(fā)現(xiàn) __i 與上述的 FREELIST_INDEX 相同,但是返回前面加了個 _S_free_list。實際上在 G2.9 中的 alloc 函數(shù)中可以找到對應代碼,在 G4.9 中把 G2.9中 的這一塊進行了封裝而已。
          分配過程每次分配一大塊內存,存到一個 free list 中,下次 client 若再有相同大小的內存要求,就直接從這個 free list 中劃出,內存釋放時,則直接回收到對應的 free list 中。
          為了管理的方便,實際分配的大小都被調整為 8 的倍數(shù),所以有 16 個 free lists,分別為 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128 bytes。例如需要 20 bytes,將會被自動調整為 24 bytes。如果超過了128字節(jié),則需要通過malloc進行分配。
          為了節(jié)省內存使用,使用 union 結構,這個設計很巧妙,每一個元素(內存區(qū)域對象)即可以當作下一個元素的指針,例如后面代碼中的 result -> _M_free_list_link,也可以當作該元素的值,例如 *__my_free_list。整個 free lists 的結構便是一串使用后者的元素構成的節(jié)點,每個節(jié)點的值是使用前者構成的單向鏈表的首地址。
          對比兩個版本,可以發(fā)現(xiàn)代碼這個內存池的頭文件,基本沒有什么變化,設計大同小異,僅僅函數(shù)名稱發(fā)生了變化!
          在 G2.9 中的 alloc 與 delloc 還是在 default_alloc_template 類中,而 G4.9 中,則是在 pool_alloc_base 的基類:__pool_alloc 中,一些核心的實現(xiàn)如下:
          template<typename _Tp>class __pool_alloc : private __pool_alloc_base{......... pointer allocate(size_type __n, const void* = 0);
          void deallocate(pointer __p, size_type __n); };

          template<typename _Tp>_Tp*__pool_alloc<_Tp>::allocate(size_type __n, const void*){ pointer __ret = 0; if (__builtin_expect(__n != 0, true)) { if (__n > this->max_size()) std::__throw_bad_alloc();
          const size_t __bytes = __n * sizeof(_Tp); if (__bytes > size_t(_S_max_bytes) || _S_force_new > 0) __ret = static_cast<_Tp*>(::operator new(__bytes)); else { _Obj* volatile* __free_list = _M_get_free_list(__bytes); _Obj* __restrict__ __result = *__free_list; if (__builtin_expect(__result == 0, 0)) __ret = static_cast<_Tp*>(_M_refill(_M_round_up(__bytes))); else { *__free_list = __result->_M_free_list_link; __ret = reinterpret_cast<_Tp*>(__result); } if (__ret == 0) std::__throw_bad_alloc(); } } return __ret;}
          template<typename _Tp>void__pool_alloc<_Tp>::deallocate(pointer __p, size_type __n){ if (__builtin_expect(__n != 0 && __p != 0, true)) { const size_t __bytes = __n * sizeof(_Tp); if (__bytes > static_cast<size_t>(_S_max_bytes) || _S_force_new > 0) ::operator delete(__p); else { _Obj* volatile* __free_list = _M_get_free_list(__bytes); _Obj* __q = reinterpret_cast<_Obj*>(__p); __q ->_M_free_list_link = *__free_list; *__free_list = __q; } }}
          下面來深入分析這兩個函數(shù)。

          3.2 allocate函數(shù)

          先來看 allocate 函數(shù):
          G2.9 分析與 G4. 9分析:在函數(shù)的一開始便定義了:
          obj* volatile *my_free_list;
          結合上圖右側的鏈表圖和上上一張圖片內容,my_free_list (對應到 G4.9 就是_free_list) 指向的是 free_list 中 16 個元素中的任何一個,*my_free_list 則取出 free_list 某元素中的值,該值指向一條分配內存的鏈表。所以 my_free_list 要定義為二級指針。result 則保存分配給用戶的一塊內存的地址。對應到 G4.9 就是 _ret。
          在 G2.9 中前面提到有一級與二級分配器,在這里體現(xiàn)就是:
          if (n > (size_t)__MAX_BYTES) { return(malloc_alloc::allocate(n));}
          if 成立就調用一級分配器,底層用 malloc 進行分配,而二級則是  if 不成立的情況.
          檢查用戶申請內存塊大小,如果大于 __MAX_BYTES(128) 那么將調用malloc_alloc::allocate(),這便是第一級分配器,這在后面分析。
          對應到 4.9 中就是:
          if (__bytes > size_t(_S_max_bytes) || _S_force_new > 0) __ret = static_cast<_Tp*>(::operator new(__bytes));
          在第一節(jié)中我們提到 operator new 底層就是 malloc,所以這兩個版本沒有什么區(qū)別。
          G4.9 中的 _S_force_new 是一個int類型,與互斥鎖相關,可暫時以不用管。
          現(xiàn)在假設用戶申請內存小于 128 字節(jié),那么將根據(jù)用戶申請內存大小分配對應的內存,由于內存池使用 free_list 鏈表管理的,每個 free_lis t鏈表元素管理不同的內存塊大小,這在前面介紹過了。
          于是在 G2.9 中:
          my_free_list = free_list + FREELIST_INDEX(n);
          對應到 G4.9 中就是:
          _Obj* volatile* __free_list = _M_get_free_list(__bytes);
          定位到該內存塊的位置,這時 my_free_list 指向的是管理該內存塊的空間的地址,使用 *my_free_list 便可以取到該內存塊的地址:
          result = *my_free_list;
          對應到 G4.9 就是:
          _Obj* __restrict__ __result = *__free_list;
          然后判斷 result 是否為空:
          if (result == 0) { void* r = refill(ROUND_UP(n)); return r;}
          如果為空,說明對應的 0-128 字節(jié)中的某鏈表(result 指向的鏈表)沒有內存,需要進行充值!
          對應到 G4.9 中就是:
          if (__builtin_expect(__result == 0, 0)) __ret = static_cast<_Tp*>(_M_refill(_M_round_up(__bytes)));
          可以發(fā)現(xiàn)原理一樣,就是名字換了而已,前面返回的是 r,這里將結果存儲到了__ret,最后做返回。
          那如果情況正常,需要將該鏈表中下一個可以使用的空間設置為當前分配給用戶空間指向的下一個、在邏輯上連續(xù)的空間,最后將result返回給用戶:
          這里做兩個動作:
          • 返回用戶區(qū)塊

          • 移動鏈表區(qū)塊指向

          *my_free_list = result->free_list_link;return (result);
          對應到 G4.9 中就是
          *__free_list = __result->_M_free_list_link;__ret = reinterpret_cast<_Tp*>(__result);
          前面的代碼對應的圖解如下:

          3.3 deallocate函數(shù)

          接著就是釋放內存函數(shù):
          static void deallocate(void *p, size_t n) //p may not be 0{ obj* q = (obj*)p; obj* volatile *my_free_list; //obj** my_free_list;
          if (n > (size_t) __MAX_BYTES) { malloc_alloc::deallocate(p, n); return; } my_free_list = free_list + FREELIST_INDEX(n); q->free_list_link = *my_free_list; *my_free_list = q;}
          上面是 G2.9 代碼,完成工作不難理解,找到需要釋放內存的那塊空間的地址,進行鏈表的頭插法即可,G4.9 同樣道理。
          在 allocate 中我們知道一級分配器是 malloc 分配,二級分配器是 free_list 管理,所以釋放內存的時候,需要做判斷,根據(jù)不同分配器來做不同處理,大于 128 字節(jié),直接free釋放掉,小于 128 字節(jié),回收到 free_list 中。
          對應到 G4.9 中我們知道沒有一級分配器,而在 allocate 中是 operator new,底層是 malloc,同樣采用 free 釋放內存,對應的就是 operator delete,小于 128 字節(jié),也是回收到 free_list 中。
          template<typename _Tp>void __pool_alloc<_Tp>::deallocate(pointer __p, size_type __n){  if (__builtin_expect(__n != 0 && __p != 0true)) { const size_t __bytes = __n * sizeof(_Tp); if (__bytes > static_cast<size_t>(_S_max_bytes) || _S_force_new > 0) ::operator delete(__p); else { _Obj* volatile* __free_list = _M_get_free_list(__bytes); _Obj* __q = reinterpret_cast<_Obj*>(__p); __q ->_M_free_list_link = *__free_list; *__free_list = __q; } }}
          上述理解圖:
          G2.9 中的一級分配器圖示:
          在最后,第一級分配器叫做:
          typedef __malloc_alloc_template<0> malloc_alloc;
          在第一級分配器中就是分配調用 malloc,釋放調用 free。其中有一個難點就是set_malloc_handler,這個函數(shù)設置的是內存分配不夠情況下的錯誤處理函數(shù),這個需要交給用戶來管理,首先保存先前的處理函數(shù),然后再將新的處理函數(shù)f賦值給 __malloc_alloc_oom_handler,然后返回舊的錯誤處理函數(shù),這也在下一張圖片中會介紹:
          可以看到 oom_malloc 函數(shù)內部做的事:該函數(shù)不斷調用__malloc_alloc_oom_handler 和 malloc 函數(shù),直到內存分配成功才返回。oom_realloc 也是如此.

          3.4 chunk_alloc函數(shù)

          到這里還剩兩個函數(shù),一個是前面的充值函數(shù) refill,一個是塊分配函數(shù) chunk_alloc 函數(shù)。
          我們先來看 chunk_alloc 函數(shù):
          上圖是 G2.9 源碼,對應的 G4.9 源碼實現(xiàn)在:
          https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/pool_allocator.cc
          函數(shù)一開始計算了一些需要的值:
          char* result;size_t total_bytes = size * nobjs;size_t bytes_left = end_free - start_free;
          對應的 G4.9 就是:
          char* __result;size_t __total_bytes = __n * __nobjs;size_t __bytes_left = _S_end_free - _S_start_free;
          完成累計總量與戰(zhàn)備池量的計算!
          result 指向分配給用戶的內存,total_bytes 為需要分配的內存塊的大小,bytes_left 則是當前戰(zhàn)備池中剩余的空間大小。
          然后判斷如果戰(zhàn)備池剩余的內存大小多余需要分配的內存塊大小,那么將內存池的首地址 start_free 直接賦值給 result,然后將 start_free 指針下移 total_bytes 距離,將當下的 result~start_free 之間的空間返回給用戶。
          if (bytes_left >= total_bytes) { result = start_free; start_free += total_bytes; return(result);}
          對應到 G4.9 就是:
          if (__bytes_left >= __total_bytes){ __result = _S_start_free; _S_start_free += __total_bytes; return __result ;}
          當然,如果 bytes_left 比 total_bytes小,但是卻比 size 大,這意味著不能直接分配 size * nobjs 大小內存給用戶,那么可以先看看內存池當下的空間能分配多少個 size 大小的塊給用戶,然后將該塊分配給用戶,start_free 指針移動 total_bytes 長度。
          else if (bytes_left >= size) { nobjs = bytes_left / size; total_bytes = size * nobjs; result = start_free; start_free += total_bytes; return(result);}
          對應到 G4.9 就是:
          else if (__bytes_left >= __n){ __nobjs = (int)(__bytes_left / __n); __total_bytes = __n * __nobjs; __result = _S_start_free; _S_start_free += __total_bytes; return __result;}
          緊接著就是內存池中有沒有多余的內存,有的話,就是碎片,處理工作為:
          if (bytes_left > 0) { obj* volatile *my_free_list = free_list + FREELIST_INDEX(bytes_left);
          ((obj*)start_free)->free_list_link = *my_free_list; *my_free_list = (obj*)start_free;}
          對應的 G4.9 就是:
          // Try to make use of the left-over piece.if (__bytes_left > 0){ _Obj* volatile* __free_list = _M_get_free_list(__bytes_left); ((_Obj*)(void*)_S_start_free)->_M_free_list_link = *__free_list; *__free_list = (_Obj*)(void*)_S_start_free;}
          然后就是不斷地獲取內存塊,將這些內存塊不斷切割用鏈表連接起來,遞歸這些過程:
          size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);start_free = (char*)malloc(bytes_to_get); if (0 == start_free) { int i; obj* volatile *my_free_list, *p;
          //Try to make do with what we have. That can't //hurt. We do not try smaller requests, since that tends //to result in disaster on multi-process machines. for (i = size; i <= __MAX_BYTES; i += __ALIGN) { my_free_list = free_list + FREELIST_INDEX(i); p = *my_free_list; if (0 != p) { *my_free_list = p -> free_list_link; start_free = (char*)p; end_free = start_free + i; return(chunk_alloc(size, nobjs)); //Any leftover piece will eventually make it to the //right free list. } } end_free = 0; //In case of exception. start_free = (char*)malloc_alloc::allocate(bytes_to_get); //This should either throw an exception or //remedy the situation. Thus we assume it //succeeded.} heap_size += bytes_to_get; end_free = start_free + bytes_to_get; return(chunk_alloc(size, nobjs));
          對應的 G4.9 就是:
          size_t __bytes_to_get = (2 * __total_bytes + _M_round_up(_S_heap_size >> 4));__try{ _S_start_free = static_cast<char*>(::operator new(__bytes_to_get));}__catch(const std::bad_alloc&){ // Try to make do with what we have. That can't hurt. We // do not try smaller requests, since that tends to result // in disaster on multi-process machines. size_t __i = __n; for (; __i <= (size_t) _S_max_bytes; __i += (size_t) _S_align) { _Obj* volatile* __free_list = _M_get_free_list(__i); _Obj* __p = *__free_list; if (__p != 0) { *__free_list = __p->_M_free_list_link; _S_start_free = (char*)__p; _S_end_free = _S_start_free + __i; return _M_allocate_chunk(__n, __nobjs); // Any leftover piece will eventually make it to the // right free list. } } // What we have wasn't enough. Rethrow. _S_start_free = _S_end_free = 0; // We have no chunk. __throw_exception_again;}_S_heap_size += __bytes_to_get;_S_end_free = _S_start_free + __bytes_to_get;return _M_allocate_chunk(__n, __nobjs);

          3.5 refill函數(shù)

          就是前面提到了32*20*2與累積量,戰(zhàn)備池中剩余量計算函數(shù),不斷為戰(zhàn)備池充值。
          對應到 G4.9 就是:
          void*__pool_alloc_base::_M_refill(size_t __n){ int __nobjs = 20; char* __chunk = _M_allocate_chunk(__n, __nobjs); _Obj* volatile* __free_list; _Obj* __result; _Obj* __current_obj; _Obj* __next_obj;
          if (__nobjs == 1) return __chunk; __free_list = _M_get_free_list(__n);
          // Build free list in chunk. __result = (_Obj*)(void*)__chunk; *__free_list = __next_obj = (_Obj*)(void*)(__chunk + __n); for (int __i = 1; ; __i++) { __current_obj = __next_obj; __next_obj = (_Obj*)(void*)((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;}
          源碼見:
          https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B98/pool_allocator.cc

          4. std::alloc概念大整理

          這里第一個是采用 list 容器,容器內會調用 alloc 的 allocate 進行分配,由于小于 128 字節(jié),會在 heap 上得到不帶 cookie 的內存,再把這一塊內存放進棧中。
          同樣的下面采用 new 關鍵字,底層調用 malloc,這樣得到就是帶 cookie,然后在放進棧中。

          5.std::alloc批斗大會

          這里談到了幾種不好的設計:
          • obj* *_free_list,*p容易使人誤解為指針的指針,實際上只等價于右邊的定義,_free_list是指針的指針,而p則是指針。

          • malloc 分配,不可重載,可以采用 operator new 來替代。

          • static void 一大堆函數(shù)指針嵌套太難理解。

          值得學習的地方
          • if 判斷將值放在變量前面,這樣可以避免少寫等號,編譯器不報錯問題。

          6.C 實現(xiàn)

          最后,由于 G2.9 的代碼變量基本是 static,所以非常容易轉為 C,侯老師給出了設計:
          ?

          ?

          瀏覽 83
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  麻豆三级片在线 | 爱AV操| 中文字幕日本欧美 | 青青草在线视频黄色 | 99re视频免费 |