歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~html
本文由[amc](cloud.tencent.com/developer/u…)發表於雲+社區專欄算法
在 C 語言的動態申請內存技術中,相比起 alloc
/free
系統調用,內存池(memory pool)是與如今系統中請求一大片連續的內存空間,而後在運行時根據實際須要分配出去的技術。使用內存池的優勢有:apache
malloc
/free
快,由於減小了系統調用的次數,特別是頻繁申請/釋放內存塊的狀況根據分配出去的內存大小,內存池能夠分爲兩類:性能優化
每次分配出去的內存單元(稱爲 unit 或者 cell)的大小爲程序預先定義的值。釋放內存塊時,則只須要簡單地掛回內存池鏈表中便可。又稱爲 「固定尺寸緩衝池」。數據結構
常規的作法是:將不一樣 unit size 的內存池整合在一塊兒,以知足不一樣內存塊大小的使用需求多線程
不分配固定長度,內存的分配只是在一大塊空閒的內存上滑動。優勢是分配效率很高,缺點是成批地回收內存,由於釋放的內存沒法直接重複利用。機器學習
使用這種須要合理規劃每塊內存的管理區域,因此又叫作 「基於區域的」 內存管理。使用這種作法的分配器,舉例有 Apache Portable Runtime 中的 apr_pool 工具。本文不討論這種內存池。函數
定長內存池有一些基本和必要的概念,須要定義在內存池的結構數據中。如下命名方式使用變體的匈牙利命名法,好比 nNext
,n
表示變量類型爲整形。相似地,p
表示指針。工具
每次程序調用 MemPool_Alloc
獲取一個內存區域後,會得到一塊連續的內存區域。管理一個這樣的內存區域的單元就成爲內存單元 unit,有時也稱做 chunk。每一個 unit 須要包含如下數據:性能
nNext
:整型數據,表示下一個可供分配的 unit 的標識號。功能請參見後問pData[]
:實際的內存區域,其大小在建立時由調用方指定一個內存塊,內存塊中保存着一系列的內存單元。
這個數據結構須要包含如下基本信息:
nSize
:整型數據,表示該 block 在內存中的大小nFree
:整型,表示剩下有幾個 unit 未被分配nFirst
:整型,表示下一個可供分配的 unit 的標識號pNext
:指針,指向下一個 memory block一個內存池總的管理數據結構,換句話說,是一個內存池對象。
pBlock
:指針,指向第一個 memory blocknUnitSize
:整型,表示每一個 unit 的尺寸nInitSize
:整型,表示第一個 block 的 unit 個數nGrowSize
:整型,表示在第一個 block 以外再繼續增長的每一個 block 的 unit 個數做爲一個內存池,須要實現如下一些基本的函數接口,或者說能夠是對象方法:
memPoolCreate()
建立一個 memory pool,必須的參數爲 unit size,可選參數爲上文 memory pool 的 nInitSize
和 nGrowSize
。
memPoolDestroy()
銷燬整個 memory pool 並交還給操做系統。
memPoolAlloc()
從 memory pool 中分配一個 unit,其尺寸是預先定義的 unit size。
memPoolFree()
釋放一個指定的 unit。
如今咱們用一個 unit size 爲 102四、init size 爲 4(每個 block 有 4 個 units)的 memory pool 爲例,解釋一下內存池的工做原理。下文假設整型的寬度爲 4 個字節。
程序開始,調用並建立一個 memory pool。此時調用的函數爲 memPoolCreate()
,程序會建立一個數據結構,相應的結構體成員及其取值以下:
當調用者第一次請求 memPoolAlloc()
時,內存池發現 block 鏈表爲空,因而想系統申請內存,建立 memory block,並初始化以下(其中地址值爲假設值):
其中 nSize = 4112 = sizeof(memPool) + nInitSize * sizeof(memUnit)
。每個 nNext
依次加一,各指代着跟着本身的下一個 unit。最後一個 unit 的 nNext
值無心義,所以不說明其取值。
而後返回須要的 unit 中的內存。返回內存的邏輯以下:
nFree
成員nFree > 0
,表示有未分配的 unit,所以繼續在該 block 中查看 nFirst
成員nFirst
等於 0,表示該 block 中位置爲 0 的 unit 可用。所以內存池能夠將這個 unit 中的 pData
地址返回給調用方。 pData
的地址值計算方式爲:pBlock + sizeof(memBlock) + nFirst * (sizeof(memUnit)) + sizeof(nNext) = 0x10010
nFree
減一nFirst
的值,標記下一個可用的 unit。注意這裏的 nFirst
切切不能簡單地加一,而是取返回給調用方的 unit 所對應的 nNext
的值,也就是下圖(2)
處原來的值 1
pData
的地址值返回。爲便於說明,這塊區域咱們標記爲 CA操做後各數據結構的狀態以下:
第二次調用 alloc
的狀況相似。調用後各數據結構的狀態以下:
咱們先看看結果:
0x10000 <= 0x10010 <= (0x10000 + 4112)
)。再計算偏移值能夠很快得出其對應的 nNext
標號,也就是上圖中的(2)
位置。nFirst
的值,參見上前幅圖,nFirst
的值爲 3,表示位置(3)
處的 unit 是可用的。所以咱們首先把 (2)
處的 nNext
值設置爲 3,將其加回到可用 unit 的鏈表中nFirst
的值修改成 0
,也就是表明剛剛回收回來的 unit 的標號,而(2)
處的值賦值爲 2,表示b(3)
的 unit其實能夠看到,上面就是一個簡單的鏈表操做。根據上面的過程,若是 CB 也釋放了的話,那麼 memory pool 的狀態則會變成這樣:
到這個時候,因爲整個 block 已經徹底回收了(nFree == nInitSize
),那麼根據不一樣的策略,能夠考慮將整個 block 從內存中釋放掉。
咱們回到 alloc
的邏輯中,能夠看到內存池最開始會檢查 block 的 nFree
成員。若是 nFree == 0
的時候,那麼就會在該 block 的 pNext
中去找到下一個 block,再去檢查 nFree
。若是發現 block 鏈表已經結束了,那就意味着當前全部的 block 已滿,必須建立新的 block。
在實際設計中,咱們須要考慮選取合適的 init size 和 grow size 值。從上面的算法中能夠看到,若是 alloc
/free
調用很是頻繁時,第一個 block 的使用效率是很是高的。
pNext
來維護鏈表,也就是隻有一個 block,而且內存的使用有一個明確且受控的上限值。這常常用在沒有 malloc
系統調用的 RTOS 或者是一些對內存很是敏感的嵌入式系統中。此文已由做者受權騰訊雲+社區發佈,更多原文請點擊
搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社區!