?????????一般我們常說(shuō)的內(nèi)存泄漏是指堆內(nèi)存的泄漏。堆內(nèi)存是指程序從堆中分配的,大小任意的(內(nèi)存塊的大小可以在程序運(yùn)行期決定),使用完后必須顯示釋放的內(nèi)存。應(yīng)用程序一般使用malloc,realloc,new等函數(shù)從堆中分配到一塊內(nèi)存,使用完后,程序必須負(fù)責(zé)相應(yīng)的調(diào)用free或delete釋放該內(nèi)存塊,否則,這塊內(nèi)存就不能被再次使用,我們就說(shuō)這塊內(nèi)存泄漏了??慈缦乱欢蜟code:












?????????以下這段小程序演示了堆內(nèi)存發(fā)生泄漏的情形:
















?????????廣義的說(shuō),內(nèi)存泄漏不僅僅包含堆內(nèi)存的泄漏,還包含系統(tǒng)資源的泄漏(resource leak),比如核心態(tài)HANDLE,GDI Object,SOCKET, Interface等,從根本上說(shuō)這些由操作系統(tǒng)分配的對(duì)象也消耗內(nèi)存,如果這些對(duì)象發(fā)生泄漏最終也會(huì)導(dǎo)致內(nèi)存的泄漏。而且,某些對(duì)象消耗的是核心態(tài)內(nèi)存,這些對(duì)象嚴(yán)重泄漏時(shí)會(huì)導(dǎo)致整個(gè)操作系統(tǒng)不穩(wěn)定。所以相比之下,系統(tǒng)資源的泄漏比堆內(nèi)存的泄漏更為嚴(yán)重。
GDI Object的泄漏是一種常見(jiàn)的資源泄漏:
?


















當(dāng)函數(shù)Something()返回非零的時(shí)候,程序在退出前沒(méi)有把pOldBmp選回pDC中,這會(huì)導(dǎo)致pOldBmp指向的HBITMAP對(duì)象發(fā)生泄漏。這個(gè)程序如果長(zhǎng)時(shí)間的運(yùn)行,可能會(huì)導(dǎo)致整個(gè)系統(tǒng)花屏。這種問(wèn)題在Win9x下比較容易暴露出來(lái),因?yàn)閃in9x的GDI堆比Win2k或NT的要小很多。
?????????有一個(gè)很簡(jiǎn)單的辦法來(lái)檢查一個(gè)程序是否有內(nèi)存泄漏.就是是用Windows的任務(wù)管理器(Task Manager).??運(yùn)行程序,然后在任務(wù)管理器里面查看 “內(nèi)存使用”和”虛擬內(nèi)存大小”兩項(xiàng),當(dāng)程序請(qǐng)求了它所需要的內(nèi)存之后,如果虛擬內(nèi)存還是持續(xù)的增長(zhǎng)的話,就說(shuō)明了這個(gè)程序有內(nèi)存泄漏問(wèn)題. 當(dāng)然如果內(nèi)存泄漏的數(shù)目非常的小,用這種方法可能要過(guò)很長(zhǎng)時(shí)間才能看的出來(lái).
?????????已經(jīng)有許多技術(shù)被研究出來(lái)以應(yīng)對(duì)這個(gè)問(wèn)題,比如Smart Pointer,Garbage Collection等。Smart Pointer技術(shù)比較成熟,STL中已經(jīng)包含支持Smart Pointer的class,但是它的使用似乎并不廣泛,而且它也不能解決所有的問(wèn)題;Garbage Collection技術(shù)在Java中已經(jīng)比較成熟,但是在c/c++領(lǐng)域的發(fā)展并不順暢,雖然很早就有人思考在C++中也加入GC的支持?,F(xiàn)實(shí)世界就是這樣的,作為一個(gè)c/c++程序員,內(nèi)存泄漏是你心中永遠(yuǎn)的痛。
以發(fā)生的方式來(lái)分類,內(nèi)存泄漏可以分為4類:
??????1.常發(fā)性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼會(huì)被多次執(zhí)行到,每次被執(zhí)行的時(shí)候都會(huì)導(dǎo)致一塊內(nèi)存泄漏。比如例二,如果Something()函數(shù)一直返回True,那么pOldBmp指向的HBITMAP對(duì)象總是發(fā)生泄漏。
??????2.偶發(fā)性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼只有在某些特定環(huán)境或操作過(guò)程下才會(huì)發(fā)生。比如例二,如果Something()函數(shù)只有在特定環(huán)境下才返回True,那么pOldBmp指向的HBITMAP對(duì)象并不總是發(fā)生泄漏。常發(fā)性和偶發(fā)性是相對(duì)的。對(duì)于特定的環(huán)境,偶發(fā)性的也許就變成了常發(fā)性的。所以測(cè)試環(huán)境和測(cè)試方法對(duì)檢測(cè)內(nèi)存泄漏至關(guān)重要。
??????3.一次性內(nèi)存泄漏。發(fā)生內(nèi)存泄漏的代碼只會(huì)被執(zhí)行一次,或者由于算法上的缺陷,導(dǎo)致總會(huì)有一塊僅且一塊內(nèi)存發(fā)生泄漏。比如,在類的構(gòu)造函數(shù)中分配內(nèi)存,在析構(gòu)函數(shù)中卻沒(méi)有釋放該內(nèi)存,但是因?yàn)檫@個(gè)類是一個(gè)Singleton,所以內(nèi)存泄漏只會(huì)發(fā)生一次。另一個(gè)例子:












如果程序在結(jié)束的時(shí)候沒(méi)有釋放g_lpszFileName指向的字符串,那么,即使多次調(diào)用SetFileName(),總會(huì)有一塊內(nèi)存,而且僅有一塊內(nèi)存發(fā)生泄漏。
??????4.隱式內(nèi)存泄漏。程序在運(yùn)行過(guò)程中不停的分配內(nèi)存,但是直到結(jié)束的時(shí)候才釋放內(nèi)存。嚴(yán)格的說(shuō)這里并沒(méi)有發(fā)生內(nèi)存泄漏,因?yàn)樽罱K程序釋放了所有申請(qǐng)的內(nèi)存。但是對(duì)于一個(gè)服務(wù)器程序,需要運(yùn)行幾天,幾周甚至幾個(gè)月,不及時(shí)釋放內(nèi)存也可能導(dǎo)致最終耗盡系統(tǒng)的所有內(nèi)存。所以,我們稱這類內(nèi)存泄漏為隱式內(nèi)存泄漏。舉一個(gè)例子:

















































?假設(shè)在Client從Server端斷開(kāi)后,Server并沒(méi)有呼叫OnClientDisconnected()函數(shù),那么代表那次連接的Connection對(duì)象就不會(huì)被及時(shí)的刪除(在Server程序退出的時(shí)候,所有Connection對(duì)象會(huì)在ConnectionManager的析構(gòu)函數(shù)里被刪除)。當(dāng)不斷的有連接建立、斷開(kāi)時(shí)隱式內(nèi)存泄漏就發(fā)生了。
二、JAVA中的內(nèi)存泄露
?????????JAVA有GC自動(dòng)回收內(nèi)存,內(nèi)存泄露是指系統(tǒng)中存在無(wú)法回收的內(nèi)存,有時(shí)候會(huì)造成內(nèi)存不足或系統(tǒng)崩潰。在C/C++中分配了內(nèi)存不釋放的情況就是內(nèi)存泄露。雖然Java存在內(nèi)存泄露,但是基本上不用很關(guān)心它,特別是那些對(duì)代碼本身就不講究的就更不要去關(guān)心這個(gè)了。 Java中的內(nèi)存泄露當(dāng)然是指:存在無(wú)用但是垃圾回收器無(wú)法回收的對(duì)象。而且即使有內(nèi)存泄露問(wèn)題存在,也不一定會(huì)表現(xiàn)出來(lái)。看下面的例子:




















































不知者無(wú)罪!Java存在內(nèi)存泄露,但是也不要夸大其辭。如果你對(duì)Java都不是很熟,你根本就不用關(guān)心這個(gè),我說(shuō)過(guò)你無(wú)意中寫(xiě)出內(nèi)存泄露的例子就像你中一千萬(wàn)一樣概率小,開(kāi)玩笑了,其實(shí)應(yīng)該是小的多的多!?
?????????在某些時(shí)候,因?yàn)榇a上寫(xiě)的有問(wèn)題,會(huì)導(dǎo)致某些內(nèi)存想回收都收不回來(lái),比如下面的代碼


