Memory Leak(記憶體洩漏)問題總結
最近聽了一些關於Memory Leak(記憶體洩漏)的seminar,感覺有些收穫,所以留個記錄,並share給朋友。
1 什麼是Memory Leak。
Memory Leak是指由於錯誤或不完備的程式碼造成一些宣告的物件例項長期佔有記憶體空間,不能回收。Memory Leak會造成系統性能下降,或造成系統錯誤。
2 Memory儲存模式
我們通常寫的C++或Java Code在記憶體裡邊的儲存狀況概如下圖。
簡單的說,一般區域性變數儲存於Stack中,以提高執行問速度。而New出來的變數則將引用資訊或指標儲存在Stack中,而將物件本身儲存與Heap區中。
這裡感謝俊曉同學看完blog後提供了這如下link,可以讓大家更好的理解什麼堆啊,棧啊之類的概念。
3 編碼產生Memory Leak的原因,及避免
Memory Leak的原因現歸納出3種,以後要還有,再做補充。
(1)No Referenced Memory (C++ only)
Sample 1
a(){
DKString* s= new DKString();
… …
… …
delete s;
}
Sample 2
a(){
char* buffer = (char*)malloc(64 * sizeof(char);
… …
… …
free(buffer);
}
C++裡邊生成/釋放變數的方式有兩種,new/delete, malloc()/free()。無論用那種方式生成,最後一定要有釋放的動作。否則,在程式離開變數作用域時,Stack裡邊的引用會被自動回收,但Heap裡的物件例項本身就將成為被永久遺棄在記憶體裡的No Referenced Memory。
另外需要注意的是, 如果用new生成的,一定要用delete釋放;如果用malloc生成的,一定要用free釋放。反之,雖然程式碼可以通過編譯,但是會造成很多潛在問題。
(2)No free Objects/Pointers (C++ and Java)
Java比C++方便的地方是Java可以自圾回收已經過期的記憶體垃圾,GC。所以,Java程式設計師從不用關心delete還是free的問題。但是碰到下面這種情況,GC也無能為力,更不要說C++了。
Sample 3
String[] sa = new String[9999999];
for (int i = 0; i < 9999999; i++){
String s = new String(“adfasdfadsfas…adfasdfa”); //a 1MB size string…
sa[i] = s;
}
這段程式碼讓GC鬱悶的是,當迴圈結束之前,GC永遠收不到任何空間。因為GC只能收集那些過期的變數,可是在sa過期之前,可能OutOfMemory已經發生了。
(3)No Limited Storage (C++ and Java)
Sample 4
… …
While (true){
Vector.add(obj);
}
… …
像 Vector, hashtable, hashmap, map, arraylist and String StringBuffer… …這樣的工具類自身沒有上限,如果,Developer再不加控制,很容易記憶體溢位。
4 如何通過測試發現Memory Leak
(1)Long Run
很多時候,微小的Memory Leak不會給我們的系統造成太多的影響,只有當洩露積累到一定程度,問題才會爆發。因此,從理論上說,我們要讓程式碼多次重複的Run從而暴露Memory Leak問題。在我們公司,這種測試叫Long Run。所謂Long Run並不一定說一定要讓測試Case跑多麼長的時間,而是跑足夠的次數。
(2)特殊Case
這是一個Tester的經驗做法,他們相信developer會在大部分正常的程式邏輯裡邊考慮和處理Memory Leak問題,但異常條件也許未必,所以,通過適當的臨界測試和特殊case測試,亦或能找到Memory Leak的case.
個人認為,發現並避免Memory Leak,最重要的還是Developer從程式設計、編碼的時候就在上游把好質量關。否則,真正到Tester發現並定位Memory Leak問題,代價則相當大。
5 分析Memory Leak的工具
工欲善其事,必先利其器。這裡會搜及並不斷補充一些Memory Leak的分析工具,以滿足Tool People的要求。
(1)Purify
一般For C Code
(2)Heap Analyzer
可以For Java Code
(3)Java Dump
這牽涉到另一個Topic,希望以後能有所補充。