一、C++
?????????一般我們常說的內存泄漏是指堆內存的泄漏。堆內存是指程序從堆中分配的,大小任意的(內存塊的大小可以在程序運行期決定),使用完后必須顯示釋放的內存。應用程序一般使用malloc,realloc,new等函數從堆中分配到一塊內存,使用完后,程序必須負責相應的調用free或delete釋放該內存塊,否則,這塊內存就不能被再次使用,我們就說這塊內存泄漏了。看如下一段Ccode:
void?GetMemory2(char?**p,?int?num)?
{?
????*p?=?(char?*)malloc(num);?
}?
void?Test(void)?
{?
????char?*str?=?NULL;?
????GetMemory(&str,?100);?
????strcpy(str,?"hello");?
????printf(str);?
}?

執行Test后,能夠輸出hello ;但是確實是內存泄露了,該free一下,另外我想C++中free is better than delete!
?????????以下這段小程序演示了堆內存發生泄漏的情形:
void?MyFunction(int?nSize)


{
???????char*??p=?new?char[nSize];

???????if(?!GetStringFrom(?p,?nSize?)?)
{
??????????????MessageBox(“Error”);
??????????????return;
???????}
???????…//using?the?string?pointed?by?p;
???????delete?p;
}


?????????廣義的說,內存泄漏不僅僅包含堆內存的泄漏,還包含系統資源的泄漏(resource leak),比如核心態HANDLE,GDI Object,SOCKET, Interface等,從根本上說這些由操作系統分配的對象也消耗內存,如果這些對象發生泄漏最終也會導致內存的泄漏。而且,某些對象消耗的是核心態內存,這些對象嚴重泄漏時會導致整個操作系統不穩定。所以相比之下,系統資源的泄漏比堆內存的泄漏更為嚴重。
GDI Object的泄漏是一種常見的資源泄漏:
?
void?CMyView::OnPaint(?CDC*?pDC?)


{
???????CBitmap?bmp;
???????CBitmap*?pOldBmp;
???????bmp.LoadBitmap(IDB_MYBMP);
???????pOldBmp?=?pDC->SelectObject(?&bmp?);
???????…

???????if(?Something()?)
{
??????????????return;
???????}
???????pDC->SelectObject(?pOldBmp?);
???????return;
}

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


{

????if(?g_lpszFileName?)
{
????????free(?g_lpszFileName?);
????}
????g_lpszFileName?=?strdup(?lpcszFileName?);
}如果程序在結束的時候沒有釋放g_lpszFileName指向的字符串,那么,即使多次調用SetFileName(),總會有一塊內存,而且僅有一塊內存發生泄漏。
??????4.隱式內存泄漏。程序在運行過程中不停的分配內存,但是直到結束的時候才釋放內存。嚴格的說這里并沒有發生內存泄漏,因為最終程序釋放了所有申請的內存。但是對于一個服務器程序,需要運行幾天,幾周甚至幾個月,不及時釋放內存也可能導致最終耗盡系統的所有內存。所以,我們稱這類內存泄漏為隱式內存泄漏。舉一個例子:
?
class?Connection


{
public:
???????Connection(?SOCKET?s);
???????~Connection();
???????…
private:
???????SOCKET?_socket;
???????…
};
class?ConnectionManager


{
public:

???????ConnectionManager()
{

???????}

???????~ConnectionManager()
{
??????????list<Connection>::iterator?it;

??????????for(?it?=?_connlist.begin();?it?!=?_connlist.end();?++it?)
{
?????????????????????delete?(*it);
??????????????}
??????????????_connlist.clear();
???????}

???????void?OnClientConnected(?SOCKET?s?)
{
?????????Connection*?p?=?new?Connection(s);
?????????_connlist.push_back(p);
???????}

???????void?OnClientDisconnected(?Connection*?pconn?)
{
??????????????_connlist.remove(?pconn?);
??????????????delete?pconn;
???????}
private:
???????list<Connection*>?_connlist;
};

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

public?class?Stack?
{
private?Object[]?elements=new?Object[10];
private?int?size?=?0;?

public?void?push(Object?e)
{
ensureCapacity();
elements[size++]?=?e;?
}

public?Object?pop()
{
if(?size?==?0)?
throw?new?EmptyStackException();?
return?elements[--size];
}

??private?void?ensureCapacity()
{

??if(elements.length?==?size)
{
??Object[]?oldElements?=?elements;
??elements?=?new?Object[2?*?elements.length+1];
??System.arraycopy(oldElements,0,?elements,?0,?size);
??}
??}
}

假如堆棧加了10個元素,然后全部彈出來,雖然堆棧是空的,沒有我們要的東西,但是這是個對象是無法回收的,這個才符合了內存泄露的兩個條件:無用,無法回收。再看這個例子:
public?class?Bad{
public?static?Stack?s=Stack();
static{
s.push(new?Object());
s.pop();?//這里有一個對象發生內存泄露
s.push(new?Object());?//上面的對象可以被回收了,等于是自愈了
}
}?因為是static,就一直存在到程序退出,但是我們也可以看到它有自愈功能,就是說如果你的Stack最多有100個對象,那么最多也就只有100個對象無法被回收其實這個應該很容易理解,Stack內部持有100個引用,最壞的情況就是他們都是無用的,因為我們一旦放新的進取,以前的引用自然消失!for example:
public?class?NotTooBad{
public?void?doSomething(){
Stack?s=new?Stack();
s.push(new?Object());
//other?code
s.pop();//這里同樣導致對象無法回收,內存泄露.
}//退出方法,s自動無效,s可以被回收,Stack內部的引用自然沒了,所以
//這里也可以自愈,而且可以說這個方法不存在內存泄露問題,不過是晚一點
//交給GC而已,因為它是封閉的,對外不開放,可以說上面的代碼99.9999%的
//情況是不會造成任何影響的,當然你寫這樣的代碼不會有什么壞的影響,但是
//絕對可以說是垃圾代碼!沒有矛盾吧,我在里面加一個空的for循環也不會有
//什么太大的影響吧,你會這么做嗎?
}上面兩個例子都不過是小打小鬧,但是C/C++中的內存泄露就不是Bad了,而是Worst了。他們如果一處沒有回收就永遠無法回收,頻繁的調用這個方法內存不就用光了!因為Java還有自愈功能(我自己起的名字,還沒申請專利),所以Java的內存泄露問題幾乎可以忽略了,但是知道的人就不要犯了。
不知者無罪!Java存在內存泄露,但是也不要夸大其辭。如果你對Java都不是很熟,你根本就不用關心這個,我說過你無意中寫出內存泄露的例子就像你中一千萬一樣概率小,開玩笑了,其實應該是小的多的多!?
?????????在某些時候,因為代碼上寫的有問題,會導致某些內存想回收都收不回來,比如下面的代碼
Temp1?=?new?BYTE[100];
Temp2?=?new?BYTE[100];
Temp2?=?Temp1;這樣,Temp2的內存地址就丟掉了,而且永遠都找不回了,這個時候Temp2的內存空間想回收都沒有辦法.