這篇文章主要介紹一下內(nèi)存池的實現(xiàn)方式,這里介紹的是一種比較經(jīng)典的內(nèi)存池實現(xiàn)方式妆兑,就是鏈表法實現(xiàn)贮预,具體原理如下:
1,首先內(nèi)存池?zé)o非是提前申請一大塊內(nèi)存片段排嫌,之后把這個片段上的指針分配給用戶畸裳,對于分配來說只要記住已經(jīng)分配的偏移量即可,每次分配將指針后移申請的內(nèi)存塊長度即可淳地。
2怖糊,主要問題在于釋放內(nèi)存,由于不確定用戶申請和釋放內(nèi)存的順序,必須假定申請和釋放都在交疊進行,
對于內(nèi)存池來說狈癞,釋放的空間必須要可以復(fù)用,否則內(nèi)存池失去了意義嚷缭;
但是先申請的內(nèi)存區(qū)域可能先釋放,這樣將出現(xiàn)釋放的區(qū)域位于已分配區(qū)域中間的情形;如何索引這些釋放的內(nèi)存片段是內(nèi)存池要主要解決的問題阅爽;
見下圖:
圖中X區(qū)域先申請路幸,當(dāng)申請了Y區(qū)域之后,釋放了X區(qū)域付翁,此時如和能夠復(fù)用X區(qū)域的內(nèi)存简肴,而不是只能使用Y區(qū)域之后的內(nèi)存時要解決的問題;
內(nèi)存池的鏈表法實現(xiàn)見第二幅圖百侧,每個申請的區(qū)域前有一個固定長度的頭部砰识,其中記錄了一些信息,最重要的是兩個指針佣渴,一個指向后面的內(nèi)存塊頭部辫狼,另一個指向前面的內(nèi)存塊頭部,并且末尾的內(nèi)存塊和首部鏈接起來辛润,構(gòu)成一個雙向環(huán)形鏈表結(jié)構(gòu)膨处;
同時還有個標(biāo)記位標(biāo)志這塊內(nèi)存有沒被使用;
當(dāng)申請內(nèi)存時砂竖,從鏈表首部開始遍歷找到第一個沒有使用的內(nèi)存塊真椿,判斷其大小是否滿足,如不滿足乎澄,繼續(xù)搜索突硝;直到滿足條件,此時分2種情形:
1置济,可用內(nèi)存塊大小和要申請的大小一樣解恰,則直接標(biāo)記為已使用,并返回即可
2舟肉,可用內(nèi)存塊大于要申請的內(nèi)存塊,則把可用內(nèi)存區(qū)間分裂為2部分查库,前一部分標(biāo)記為已使用路媚,后一部分標(biāo)記為未使用,并繼續(xù)保持它們的鏈表結(jié)構(gòu)樊销;
對于釋放內(nèi)存整慎,需要判斷是否前面和后面相鄰的內(nèi)存塊有未使用,如果有围苫,則需要合并裤园,這一步至關(guān)重要,因為如果存在大量相鄰的標(biāo)記未使用內(nèi)存可能會導(dǎo)致無法分配一塊較大區(qū)域剂府,
以上操作始終保持鏈表結(jié)構(gòu)拧揽,就可以實現(xiàn)一個基礎(chǔ)內(nèi)存池了;
實際上,這個實現(xiàn)思路來自于John Carmack在Quake3中的實現(xiàn)
但是我們可以稍微做一點優(yōu)化(不過要犧牲一些額外的存儲空間)優(yōu)化方法是淤袜,額外記錄兩個指針痒谴,以環(huán)形形式連接所有未使用的內(nèi)存空間,這樣铡羡,查找的效率提高了积蔚,幾乎提升到了常數(shù)時間,
同時這個內(nèi)存池還有一個缺陷就是長度固定烦周,不過順著這個思路尽爆,我們可以在內(nèi)存不夠時,新分配一塊大的區(qū)域读慎,并且保持原來的鏈表結(jié)構(gòu)即可漱贱,
下面附上代碼實現(xiàn),見
https://github.com/wiltchamberian/MemoryPool
這里實現(xiàn)了兩個贪壳,第一個是參考Quake3 Carmack的實現(xiàn)饱亿,應(yīng)該無問題;
第二個SuperMemoryPool是改良后的實現(xiàn)闰靴,改良版實現(xiàn)的速度要提高了一些彪笼,支持動態(tài)內(nèi)存