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