作服務器開發的相信多數都知道內存池,一個廣泛認識是內存池是爲了消除內存碎片而出現的,而就個人經驗來看,其實並非這樣。 服務器
1.內存池主要做用是提升分配回收效率,由於每次new delete都要進入到內核,是一個效率相對較低的操做。 測試
2.系統中不用內存池,也不會產生大量的內存碎片 spa
本文主要分析講解第2點,爲何不會產生大量的內存碎片,提早回答標題,內存池就是爲了提升分配效率。 設計
要否認一個觀點,首先要知道這個觀點的造成緣由,那麼廣泛認爲的碎片的產生,是因爲大量的new/delete形成,系統new操做是找到最近的一個足夠大的連續空間分配出去,若是某個較小的內存被delete回收到系統,而以後發生的new操做須要更大尺寸的內存,因而較小的內存被棄用成爲所謂的內存碎片。 對象
以上觀點要想成立,須要一個前提,那就是程序中每次new出來的內存大小都不同,且delete比new少。 遊戲
這裏提出一個概念——有限對象需求 事件
所謂「有限對象需求」,是程序的一個通性,無論什麼程序,同一時刻須要的使用的對象數量與種類都是有限的。 內存
而支撐內存碎片出現的前提,就是一個「無限對象需求」的程序模型,是實際程序設計中是不存在的,因此說不使用內存池也不會形成大量內存碎片,爲何不是0碎片,碎片仍是會有,可是存在時間很是短,很快就會被從新吸取利用。 服務器開發
舉個例子來看看有限對象需求下,內存碎片的瞬間產生,瞬間消失的過程 開發
假設你的程序中須要頻繁的經過new產生,而後delete的對象,尺寸從小到大依次存在a,b,c,d,e...等有限數量的類型。
下面開始無數次new/delete的循環中,看看碎片到底去了哪裏。
可能不很容易懂,先大體瀏覽下留個印象,下面我會經過簡明文字進行說明
看以下new/delete序列爲
new n次a 如下x次y對象表達爲x*y
new m*b 與n個a相鄰
delete m*a(m<n)
new x*c 產生(n-m)*a - q*c(0 <= q <= x)大小的碎片?前次delete空出的的連續內存,可能能夠容納q個c,剩餘部分紅爲碎片?
大概幾秒後發生如下事件,內存碎片天然消失
new y*d 與x-q個c相鄰
delete (n-m)*a 全部的a被釋放
delete m*b 全部的b被釋放
new x*a 可能所有在一個連續區域,可能分紅2部分,1部分是(n-m)*a以前釋放的,一部分是(x-(n-m))*a
delete xx*c xx<x
delete y*d
new x*a
delete (x-xx)*c 全部的c所有被釋放,以前產生的(n-m)*a - q*c(0 <= q <= x)大小的碎片從新恢復成(n-m)*a的連續區域
new x*a 此x非前面的x,只是個代號,不要作大小等同,從新利用(n-m)*a的連續區域,碎片從新被利用
好了,所謂的碎片消失了
上面的過程演示了一個程序中(能夠是服務器也能夠是遊戲client),頻繁無序的new/delete操做過程當中,內存碎片產生又再在短期內消失的過程。
核心原理就是
碎片產生:n個大對象的new佔用了m個小對象所能使用的內存段,致使暫時的內存碎片出現
碎片消失:n大對象的delete,n*大對象+碎片,從新恢復了m個小對象的大小,能夠從新被利用於小對象的new操做。
這就是爲了避免用內存池也不會產生內存歲片的緣由,除非你的n個大對象永遠不delete纔會真的產生碎片,但那就不是內存碎片,而是內存泄露了,內存泄露不是內存池能解決的,即便使用內存池,結果也是致使內存池不斷變大,最終吃光內存。
再來看看實際程序開發中的new/delete狀況是否符合上面的所說的「有限對象需求」這個特性
1.無論什麼程序,須要new的對象的種類是有限,無限的話,一個對象一個class,你的程序代碼就永遠無法寫完了
2.同一時刻須要使用的對象數量有限
對象的需求來源於程序執行的任務的多少,而一個軟件系統中,同一時刻任務數量確定是有限的,前一匹任務執行完畢前,新的任務不會開始,不然系統就該被負荷壓跨了。
以上只是理論分析,下面看程序驗證,只要驗證系統new是選擇最近的足夠大的連續空間。
int main(int argc, char* argv[]) {
int *p = new int; //斷點查看結果,在個人機器上p地址爲441830
char *c = new char; //斷點查看結果,在個人機器上c地址爲441800
int *q = new int; //斷點查看結果,在個人機器上q地址爲4417d0
delete p;
delete c;
c = new char; //斷點查看結果,在個人機器上c地址爲441830,佔用了早前p獲得的內存塊,最近的足夠大的連續內存
p = new int; //斷點查看結果,在個人機器上p地址爲441800,佔用了c第一new時獲得的內存塊(第2塊內存)
return 0;
}
以上只是簡單測試代碼,有興趣的朋友能夠,本身編寫一些更復雜的好比帶循環的進行測試,歡迎共同研究討論
最後若是你發現本身的程序中碎片不少,按照有限對象需求模型的原理——只有當對象沒有被delete時纔會致使碎片沒法恢復,就能夠知道必定是內存泄露了。