天天看點

【C/C++】Boost::pool記憶體鍊/池

目錄

​​Boost::pool說明​​

​​boost::pool 的實作原理​​

​​部分源碼​​

​​總結​​

​​經驗​​

​​使用boost::pool<>遇到嚴重的性能問題​​

Boost::pool說明

 ​

pool去按照一定的增長規則,從作業系統申請一大塊記憶體,稱為block,源碼中用PODptr表示。

這個PODptr結構将block分為三塊:

【C/C++】Boost::pool記憶體鍊/池

第一塊是大塊資料區(後面會格式化為許多個小塊chunk)

第二塊隻有sizeof(void*) 個位元組,即指針大小,儲存下一個PODptr的指針

第三塊儲存下一PODptr的長度。

最後一個PODptr指針為空。

PODptr的資料區被simple_segregated_storage格式化為許多個小塊,稱為chunk。一個chunk的大小是定義boost::object_pool時決定的,即 sizeof(T)>sizeof(void)?sizeof(T):sizeof(void)。

任意一個chunk未被占用時,使用其前sizeof(void*)個位元組作為一個指針指向下一個未被占用的chunk。是的,單向連結清單。

【C/C++】Boost::pool記憶體鍊/池

而從pool::malloc,就執行單向連結清單的删除節點操作,每次都傳回首個chunk,是以未進行重新申請block前,malloc都是O(1)。

pool::free(ptr)操作就是找到ptr屬于哪個PODptr,然後把ptr添加到單向連結清單頭。

pool::ordered_free(ptr)找到ptr屬于哪個PODptr,然後通過插入排序把ptr添加到單向連結清單。

simple_segregated_storage

//segregate會把給的一個sz大小的記憶體塊,拆分為每個partition_sz大小的多個chunk單元,

//每個chunk的前4位元組指向下一個chunk(作為連結清單的next),而最後一個chunk頭指向end。

【C/C++】Boost::pool記憶體鍊/池

下段代碼從simple_segregated_storage連結清單中擷取記憶體:

class PODptr

【C/C++】Boost::pool記憶體鍊/池

如上圖,類PODptr訓示了一個block結構,這個block大小不一定相同,但都由 chunk data+ next ptr + next block size三部分組成。

chunk data部分被構造成一個simple_segregated_storage,切分為多個chunk,是一塊連續的記憶體

next ptr 指向下一個block結構,next block size指出了下一個block結構的大小。

也就是說,多個PODptr結構組成一個連結清單,而PODptr内部由simple_segregated_storage分成一個順序表。

PODptr的大小不固定,增長方式見​<code>​void * pool&lt;UserAllocator&gt;::malloc_need_resize()​</code>​.

初始化的每個chunk都指向下一個chunk

class pool

在調用pool::malloc隻申請一個chunk時,如果有足夠空間,使用父類指針調用malloc傳回記憶體,否則就重新申請一個大block。代碼簡單,就不貼了。

下面代碼是申請n個連續的chunk。如果沒有連續的n個記憶體就需要重新配置設定記憶體了。配置設定好的記憶體,通過add_ordered_block添加到chunks的有序連結清單,并通過位址大小把剛申請的block放到PODptr連結清單的排序位置。

下面代碼是釋放未被占用的塊。(一個block任何一個chunk被占用就不會釋放)

pool總結

pool的實作基本就是利用simple_segregated_storage内部實作的維護chunk的連結清單來實作記憶體管理的。simple_segregated_storage可以說是pool的核心。pool内部一共維護了兩個連結清單:

simple_segregated_storage内部的chunk連結清單。配置設定單個chunk時,直接從這個連結清單拿一個chunk,複雜度O(1)。

pool内部有個成員變量​<code>​details::PODptr&lt;size_type&gt; list;​</code>​用來維護一個大塊記憶體block的連結清單。可以知道,一個block内部是連續的,但block之間可以認為是不連續的記憶體。這個連結清單相當于一個記憶體位址索引,主要是為了提高查找效率:對于有序排列的記憶體池,歸還記憶體時,用來快速判斷是屬于哪個塊的。如果沒有這個連結清單,就需要挨個chunk去判斷位址大小。

class object_pool

object_pool繼承自pool,但和pool的差別是,pool用于申請固定大小的記憶體,而object_pool用于申請固定類型的記憶體,并會調用構造函數和析構函數。主要的函數就兩個:

調用構造函數,用到了一個placement new的方式,老生常談。

唯一需要注意的是construct和destroy調用的malloc和free,都是調用的 ​<code>​ordered_malloc​</code>​ 和 ​<code>​ordered_free​</code>​。

destroy顯式調用析構函數去析構,然後把記憶體還給連結清單維護。

class singleton_pool

單例記憶體池的實作,值得注意的有如下幾點:

單線程使用單例時(保證無同步問題),可以通過定義宏BOOST_POOL_NO_MT來取消同步的損耗。

單例記憶體池的單例實作如下,通過内部類object_creator調用private函數get_pool(),通過create_object.do_nothing();來保證在main之前執行個體化靜态對象​<code>​static object_creator create_object;​</code>​

适用範圍:頻繁申請釋放相同大小的記憶體,如需要頻繁的建立同一個類的對象。

優點:可以防止記憶體碎片、極快,避免頻繁申請記憶體的調用.

boost::pool 的源代碼一共就幾個檔案,簡潔明了,讀起來也不很難。由于代碼時間遠早于現代C++(C++11之後)成型,相容編譯器的代碼建議忽略。因為重要的是其設計思想:如何通過自構兩個連結清單來提升記憶體管理效率的。

資料結構很簡單。适用場景比較狹窄,跟GC沒法比。

經驗

繼續閱讀