一种高效快速的内存池实现(附源码)
此算法靈感來自于apache內存池實現原理,不過讀者如果沒有看過apache內存池實現也無關系,因為本算法相對apache內存池算法更為簡單而且易懂,個人認為某些場合也更為高效,或許真正到了apache服務器上性能不如,但是這套設計思想應該還是可以借鑒到更多場合的。
?
我們在調用malloc函數時,操作系統內部會查找一個所謂的空閑鏈表,當找到足夠大的空閑空間時會將內存分割并返回一部分會用戶,當然在很大的項目里面有可能會出現鏈表所有節點都找不到空閑空間的情形,此時操作系統便會不斷搜索內存碎片,然后組合成一段足夠大的空間并返回,如果此時還找不到便返回NULL。所以在new或者malloc調用后程序員有必要對申請的內存做是否為NULL的判斷。而且在調用free后系統有可能還要合并內存產生額外開銷,另外不斷的malloc和free也會產生很多內存碎片并給操作系統管理內存帶來很大的壓力。所以說內存池算法在很多場合是非常必須的。
該內存池結構可以初步理解成一個二維數組,每個元素都是內存塊。每個內存塊都有一段附帶的數據信息,代表這塊內存在整個二維數組中的位置。申請和釋放的時候可以根據附帶信息直接找到內存塊在內存池也就是這個二維數組中的位置。
?
以上A1到A8為例,假設整個內存池序列有n個,也就是A1到An,絕對會存在m(1<=m<=n),保證A1到Am為空閑狀態,Am到An為被使用狀態。
?
也就是說,如果內存池A1到An全部沒有被使用,當申請內存時,只需要檢查A1是否為空閑狀態即可。有的可以返回,如果不是空閑,說明整個A序列內存都已經被占用了。當A1空閑,那么申請內存后將A1狀態標記為被使用狀態,然后將A1和An調換,此時保證內存池前n-1為未使用狀態,n為使用狀態。同樣的道理,再次申請則將調整后的A1和A(n-1)調換,保證A1到A(n-2)為空閑狀態,最后兩個元素為被使用狀態。
?
釋放內存時,假設內存池序列是A1到Am為空閑,A(m+1)到An為被使用,而被使用的內存肯定是隨機在m+1到n的某個元素,那么將其標記為未使用狀態之后馬上與m+1這個元素調換,此時保證A1到A(m+1)空閑,后A(m+2)到An為未使用。
?
所以說內存池中不管怎么申請釋放或者調整,始終保證A1到Am空閑,之后的為被使用狀態。如果中間有元素太長時間沒被使用而釋放,此時也需要根據這規則做調整。因為這樣申請內存時不需要查表就可以找到元素,而釋放內存時也能根據內存池管理數據結構中的外部和內部索引一步找到內存池中的位置,這也是該內存池高效的原因之一。
?
內存塊附帶的數據結構如下:
struct MemPoolData {unsigned int dwMemTrunkTicket;unsigned char chbIsMemTrunkUsed;unsigned char chOutIndex;unsigned char chInIndex; };?
其中包含了該內存塊是否被占用,上次使用時間戳以及在二維數組中的位置。
?
那么按照以上規則,假如說A1到A8都已經滿員而且全都沒有被使用,再次申請8字節內存時,首先檢查A1這塊內存是否已經被使用,如果被使用就無法申請。但是如果沒有被使用則返回給程序,標記此內存已經被使用。然后將A8內存和A1調換,記錄一個索引7,代表A1到A7沒有被使用,A7以后的數據被使用了。同樣的道理再次申請時仍然直接檢查A1,因為沒被使用則返回給程序,再將A1與A7調換,此時A1到A6沒有被使用,A6到A8被使用了。釋放內存時,假如說A1到A3未被使用而A4到A8被使用,釋放的內存是A6,則將A6標記為未使用狀態,將A4與A6調換即可。
?
另外如果申請和釋放內存時,系統函數會鎖住整個內存管理隊列,而這套算法只會鎖住當前大小序列,也就是說,申請8字節大小和申請16字節大小內存時不會產生鎖競爭。因為8字節大小操作只是鎖住A序列,16大小則是鎖住圖上的B序列。每個序列都有自己獨立的鎖。這也是高效快速的原因之一。以上就是申請和釋放內存的基本思想,但是實際上向下的數組和自己實現和vector類似的容器,這樣可以自動拓展,具體實現方法可以參考內存池源碼。
?
源碼:MemPoolTest.zip
轉載于:https://www.cnblogs.com/mod109/p/5004474.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的一种高效快速的内存池实现(附源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MFC 内部组织结构(简单单文档)
- 下一篇: (软件工程复习核心重点)第三章需求分析-