1. 程式人生 > >C++標準庫——STL之空間配置器

C++標準庫——STL之空間配置器

但是 chunk 內容 既然 部分 如何 標識 stl源碼 strong

聲明:源碼同《STL源碼剖析》(侯捷)

STL:

  C++標準的模板庫,通用性高。

  常見的數據結構封裝。

  提供常用的通用算法。

STL六大組件:

  容器 算法 叠代器 仿函數(函數對象) 適配器 配置器

空間配置器的作用:

  1.提高代碼復用率,功能模塊化。

  2.減少內存碎片問題。

    比如我們list是鏈式結構,如果其中的成員是一個個new出來的,我們知道new的底層實現是malloc,同時也必須清楚,當malloc一塊40字節空間時,操作系統為了能方便找到這塊空間還會加一個結構在在這塊空間中,所以開辟的空間不止40字節(應該是40+36,vs2013下)。空間利用率低。

  3.效率問題:頻率的分配小塊內存,效率比較低。

  4.容易造成內存泄漏。

    統一管理更加方便。

  5.調用malloc/new向系統分配的每塊內存都有一些額外開銷。

  6.空間不足時的應對措施也合理。

    一級空間配置器源碼底層有一個__malloc_alloc_template模板類,裏面封裝了一些函數以應對空間不足的情況。

 技術分享圖片

  7.解決部分存在線程安全的問題

  I.最初空間配置器的內部其實就是封裝重載了new運算符。

  底層源碼框架制定死了,所以只能通過它給的接口調用相關函數。

  比如allocate()函數就是用於分配內存空間,就是寫死的,你只能用它。

  II.處理內存碎片的問題時所用的是二級空間配置器。利用內存池的概念。

  當使用空間配置器時有以下過程:

  1.源碼中存在一個枚舉變量__MAX_BYTES(128)來區分大內存塊和小內存塊,凡是大於__MAX_BYTES的內存塊都是屬於一級空間配置器的管理範疇。

  2.二級空間配置器的底層是由一個free list(哈希桶)來管理的。有一個枚舉變量__ALIGN的值(8)就是表示哈希桶劃分的間隔。

  3.二級空間配置器中會有對應算法來計算出最合適的內存塊和內存塊個數,然後查詢哈希桶是否有對應的內存塊或者說內存塊是否足夠,如果沒有,向系統詢問調整free list。

    技術分享圖片

    如何調用?——refill()函數。

  4.在allocate()函數調用時發現free list中沒有空間是,便會呼叫refill()函數重新填充free list。——去找內存池要。

    技術分享圖片

    新的空間來自哪?——內存池。這部分工作交給chunk_alloc()。當然也有可能內存池(memory pool)中內存也滿足不了要求。

    類中利用兩個靜態指針 start_free 和 end_free來標識內存池。

    chunk_alloc()會利用end_free - start_free來計算判斷內存池的內存量:

      如果存在內存但是不夠,就先把能給的給出去。

      如果存在內存而且夠了,直接給不廢話。

      如果內存是一點都沒了,只能重新去heap中malloc。

  5.山窮水盡——如果真的二級空間配置器一點內存都分配不出來了,那麽只能從一級空間配置器中來尋找。

  為什麽二級都沒有而一級會有呢?

  因為一級中有空間不足時的應對措施,所以就算是失敗,也是一級空間配置器來報的錯。

  解惑:

  為什麽向上對齊ROUND_UP是八個字節而不是四個字節?

  因為我們知道裏面的內容是一塊單鏈表,那麽肯定有一個指針,那麽為了符合64位系統,肯定是8個字節。

  默認情況下用一級空間配置器還是二級空間配置器?

  二級空間配置器。源碼中有一個模板類——simple_alloc。

  其中有一個宏——__USE_MALLOC ,如果這個宏被定義了,那麽就是用一級空間適配器。

  如果沒被定義,那麽就是用二級空間適配器。

  這個宏就是決定,誰的別名時alloc。

  而alloc就是STL標準庫中默認類參數的成員——

  template <class T,class Alloc = alloc>

  class list{

    ...

  };

  技術分享圖片

  技術分享圖片 

  空間配置器底層使用malloc實現的,所以他定義出來的東西並不是一個對象(malloc不調用構造函數)。

  那麽STL標準庫開辟空間時是如何才能構建出對象?

  利用定位new表達式,既然開辟了一個空間p,直接new(p) T1(value) 即可。

  所以如果我們要構造STL對象和銷毀STL對象分下列部分:

  1.get_node 獲取空間——construct 構造對象(定位new)

  2.destroy 銷毀對象——put_node 歸還空間,如何歸還看下一問。

  二級空間配置器的釋放?

  首先先判斷是否大於128,如果大於則用一級空間配置器的釋放,如果小於則掛回free list。

  

  我們了解了construct和destroy是構造和銷毀,那麽拷貝構造,整體復制,按指定個數復制,都會有定義——

  即uninitialized_copy(),uninitialized_fill(),uninitialized_fill_n()。

  詳見《STL源碼剖析》,這裏不細說,其底層也是運動叠代器在[first,last)上遍歷。

C++標準庫——STL之空間配置器