「該死系統存在內存泄漏問題」,項目中因爲各方面因素,老是有人抱怨存在內存泄漏,系統長時間運行以後,可用內存愈來愈少,甚至致使了某些服務失敗。內存泄漏是最難發現的常見錯誤之一,由於除非用完內存或調用malloc失敗,不然都不會致使任何問題。實際上,使用C/C++這類沒有垃圾回收機制的語言時,你不少時間都花在處理如何正確釋放內存上。若是程序運行時間足夠長,如後臺進程運行在服務器上,只要服務器不宕機就一直運行,一個小小的失誤也會對程序形成重大的影響,如形成某些關鍵服務失敗。php
對於內存泄漏,本人深有體會!實習的時候,公司一個項目中就存在內存泄漏問題,項目的代碼量很是大,後臺進程也比較多,形成內存泄漏的地方比較難找。此次機會是我對如何查找內存泄漏問題,有了必定的經驗,後面本身的作了相關實驗,在此我分享一下內存泄漏如何調試查找,主要內容以下:html
其實Windows、Linux下面的內存檢測均可以單獨開篇詳細介紹,方法和工具也遠不止文中介紹到的,個人方法不是最優的,若是您有更好的方法,也請您告訴我和你們。ios
wikipedia中這樣定義內存泄漏:在計算機科學中,內存泄漏指因爲疏忽或錯誤形成程序未能釋放已經再也不使用的內存的狀況。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存後,因爲設計錯誤,致使在釋放該段內存以前就失去了對該段內存的控制,從而形成了內存的浪費。程序員
最難捉摸也最難檢測到的錯誤之一是內存泄漏,即未能正確釋放之前分配的內存的 bug。 只發生一次的小的內存泄漏可能不會被注意,但泄漏大量內存的程序或泄漏日益增多的程序可能會表現出各類徵兆:從性能不良(而且逐漸下降)到內存徹底用盡。 更糟的是,泄漏的程序可能會用掉太多內存,以至另外一個程序失敗,而使用戶無從查找問題的真正根源。 此外,即便無害的內存泄漏也多是其餘問題的徵兆。編程
內存泄漏會由於減小可用內存的數量從而下降計算機的性能。最終,在最糟糕的狀況下,過多的可用內存被分配掉致使所有或部分設備中止正常工做,或者應用程序崩潰。內存泄漏可能不嚴重,甚至可以被常規的手段檢測出來。在現代操做系統中,一個應用程序使用的常規內存在程序終止時被釋放。這表示一個短暫運行的應用程序中的內存泄漏不會致使嚴重後果。服務器
在如下情況,內存泄漏致使較嚴重的後果:less
下面咱們經過如下例子來介紹如何檢測內存泄漏問題:函數
1 #include <stdlib.h> 2 #include <iostream> 3 using namespace std; 4 5 void GetMemory(char *p, int num) 6 { 7 p = (char*)malloc(sizeof(char) * num);//使用new也可以檢測出來 8 } 9 10 int main(int argc,char** argv) 11 { 12 char *str = NULL; 13 GetMemory(str, 100); 14 cout<<"Memory leak test!"<<endl; 15 //若是main中存在while循環調用GetMemory 16 //那麼問題將變得很嚴重 17 //while(1){GetMemory(...);} 18 return 0; 19 }
實際中不可能這麼簡單,若是這麼簡單也用不着別的方法,程序員一眼就能夠看出問題,此程序只用於測試。工具
Windows平臺下面Visual Studio調試器和C運行時(CRT)庫爲咱們提供了檢測和識別內存泄漏的有效方法,原理大體以下:內存分配要經過CRT在運行時實現,只要在分配內存和釋放內存時分別作好記錄,程序結束時對比分配內存和釋放內存的記錄就能夠肯定是否是有內存泄漏。在vs中啓用內存檢測的方法以下:性能
1 #define _CRTDBG_MAP_ALLOC 2 #include <stdlib.h> 3 #include <crtdbg.h>
經過包括 crtdbg.h,將 malloc 和 free 函數映射到它們的調試版本,即 _malloc_dbg 和 _free_dbg,這兩個函數將跟蹤內存分配和釋放。 此映射只在調試版本(在其中定義了_DEBUG)中發生。 發佈版本使用普通的 malloc 和 free 函數。
#define 語句將 CRT 堆函數的基版本映射到對應的「Debug」版本。 並不是絕對須要該語句;但若是沒有該語句,內存泄漏轉儲包含的有用信息將較少。
_CrtDumpMemoryLeaks();
此時,完整的代碼以下:
1 #define _CRTDBG_MAP_ALLOC 2 #include <stdlib.h> 3 #include <crtdbg.h> 4 5 #include <iostream> 6 using namespace std; 7 8 void GetMemory(char *p, int num) 9 { 10 p = (char*)malloc(sizeof(char) * num); 11 } 12 13 int main(int argc,char** argv) 14 { 15 char *str = NULL; 16 GetMemory(str, 100); 17 cout<<"Memory leak test!"<<endl; 18 _CrtDumpMemoryLeaks(); 19 return 0; 20 }
當在調試器下運行程序時,_CrtDumpMemoryLeaks 將在「輸出」窗口中顯示內存泄漏信息。 內存泄漏信息以下所示:
Detected memory leaks! Dumping objects -> d:\documents\visualstudio2013\projects\memoryleak\memoryleak\main.cpp(10) : {164} normal block at 0x0033C778, 100 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete. 程序「[8800] MemoryLeak.exe」已退出,返回值爲 0 (0x0)。
若是沒有使用#define _CRTDBG_MAP_ALLOC 語句,內存泄漏轉儲將以下所示:
Detected memory leaks! Dumping objects -> {164} normal block at 0x0076C778, 100 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete. 程序「[12384] MemoryLeak.exe」已退出,返回值爲 0 (0x0)。
未定義_CRTDBG_MAP_ALLOC 時,所顯示的會是:
從不會在內存泄漏信息中看到下面兩種塊類型:
定義了 _CRTDBG_MAP_ALLOC 時,還會顯示在其中分配泄漏的內存的文件。 文件名後括號中的數字(本示例中爲 10)是該文件中的行號。
注意:若是程序老是在同一位置退出,調用 _CrtDumpMemoryLeaks 將很是容易。 若是程序從多個位置退出,則無需在每一個可能退出的位置放置對 _CrtDumpMemoryLeaks 的調用,而能夠在程序開始處包含如下調用:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
該語句在程序退出時自動調用 _CrtDumpMemoryLeaks。 必須同時設置 _CRTDBG_ALLOC_MEM_DF 和_CRTDBG_LEAK_CHECK_DF 兩個位域,如前面所示。
經過上面的方法,咱們幾乎能夠定位到是哪一個地方調用內存分配函數malloc和new等,如上例中的GetMemory函數中,即第10行!可是不能定位到,在哪一個地方調用GetMemory()致使的內存泄漏,並且在大型項目中可能有不少處調用GetMemory。如何要定位到在哪一個地方調用GetMemory致使的內存泄漏?
定位內存泄漏的另外一種技術涉及在關鍵點對應用程序的內存狀態拍快照。 CRT 庫提供一種結構類型 _CrtMemState,您可用它存儲內存狀態的快照:
_CrtMemState s1, s2, s3;
若要在給定點對內存狀態拍快照,請向 _CrtMemCheckpoint 函數傳遞 _CrtMemState 結構。 該函數用當前內存狀態的快照填充此結構:
_CrtMemCheckpoint( &s1 );
經過向 _CrtMemDumpStatistics 函數傳遞 _CrtMemState 結構,能夠在任意點轉儲該結構的內容:
_CrtMemDumpStatistics( &s3 );
若要肯定代碼中某一部分是否發生了內存泄漏,能夠在該部分以前和以後對內存狀態拍快照,而後使用 _CrtMemDifference 比較這兩個狀態:
1 _CrtMemCheckpoint( &s1 ); 2 // memory allocations take place here 3 _CrtMemCheckpoint( &s2 ); 4 5 if ( _CrtMemDifference( &s3, &s1, &s2) ) 6 _CrtMemDumpStatistics( &s3 );
顧名思義,_CrtMemDifference 比較兩個內存狀態(s1 和 s2),生成這兩個狀態之間差別的結果(s3)。 在程序的開始和結尾放置 _CrtMemCheckpoint 調用,並使用_CrtMemDifference 比較結果,是檢查內存泄漏的另外一種方法。 若是檢測到泄漏,則可使用 _CrtMemCheckpoint 調用經過二進制搜索技術來劃分程序和定位泄漏。
如上面的例子程序咱們能夠這樣來定位確切的調用GetMemory的地方:
1 #define _CRTDBG_MAP_ALLOC 2 #include <stdlib.h> 3 #include <crtdbg.h> 4 5 #include <iostream> 6 using namespace std; 7 8 _CrtMemState s1, s2, s3; 9 10 void GetMemory(char *p, int num) 11 { 12 p = (char*)malloc(sizeof(char) * num); 13 } 14 15 int main(int argc,char** argv) 16 { 17 _CrtMemCheckpoint( &s1 ); 18 char *str = NULL; 19 GetMemory(str, 100); 20 _CrtMemCheckpoint( &s2 ); 21 if ( _CrtMemDifference( &s3, &s1, &s2) ) 22 _CrtMemDumpStatistics( &s3 ); 23 cout<<"Memory leak test!"<<endl; 24 _CrtDumpMemoryLeaks(); 25 return 0; 26 }
調試時,程序輸出以下結果:
0 bytes in 0 Free Blocks. 100 bytes in 1 Normal Blocks. 0 bytes in 0 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 0 bytes. Total allocations: 100 bytes. Detected memory leaks! Dumping objects -> d:\documents\visualstudio2013\projects\memoryleak\memoryleak\main.cpp(12) : {164} normal block at 0x0078C778, 100 bytes long. Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD Object dump complete. 程序「[10264] MemoryLeak.exe」已退出,返回值爲 0 (0x0)。
這說明在s1和s2之間存在內存泄漏!!!若是GetMemory不是在s1和s2之間調用,那麼就不會有以下信息輸出。
0 bytes in 0 Free Blocks. 100 bytes in 1 Normal Blocks. 0 bytes in 0 CRT Blocks. 0 bytes in 0 Ignore Blocks. 0 bytes in 0 Client Blocks. Largest number used: 0 bytes. Total allocations: 100 bytes.
在上面咱們介紹了,vs中在代碼中「包含crtdbg.h,將 malloc 和 free 函數映射到它們的調試版本,即 _malloc_dbg 和 _free_dbg,這兩個函數將跟蹤內存分配和釋放。 此映射只在調試版本(在其中定義了_DEBUG)中發生。 發佈版本使用普通的 malloc 和 free 函數。」即爲malloc和free作了鉤子,用於記錄內存分配信息。
Linux下面也有原理相同的方法——mtrace,http://en.wikipedia.org/wiki/Mtrace。方法相似,我這就不具體描述,參加給出的連接。這節我主要介紹一個很是強大的工具valgrind。以下圖所示:
如上圖所示知道:
==6118== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==6118== at 0x4024F20: malloc (vg_replace_malloc.c:236)
==6118== by 0x8048724: GetMemory(char*, int) (in /home/netsky/workspace/a.out)
==6118== by 0x804874E: main (in /home/netsky/workspace/a.out)
是在main中調用了GetMemory致使的內存泄漏,GetMemory中是調用了malloc致使泄漏了100字節的內存。
Things to notice:
• There is a lot of information in each error message; read it carefully.
• The 6118 is the process ID; it’s usually unimportant.
• The first line ("Heap Summary") tells you what kind of error it is.
• Below the first line is a stack trace telling you where the problem occurred. Stack traces can get quite large, and be
confusing, especially if you are using the C++ STL. Reading them from the bottom up can help.
• The code addresses (eg. 0x4024F20) are usually unimportant, but occasionally crucial for tracking down weirder
bugs.
The stack trace tells you where the leaked memory was allocated. Memcheck cannot tell you why the memory leaked,
unfortunately. (Ignore the "vg_replace_malloc.c", that’s an implementation detail.)
There are several kinds of leaks; the two most important categories are:
• "definitely lost": your program is leaking memory -- fix it!
• "probably lost": your program is leaking memory, unless you’re doing funny things with pointers (such as moving
them to point to the middle of a heap block)
Valgrind的使用請見手冊http://valgrind.org/docs/manual/manual.html。
其實內存泄漏的緣由能夠歸納爲:調用了malloc/new等內存申請的操做,但缺乏了對應的free/delete,總之就是,malloc/new比free/delete的數量多。咱們在編程時須要注意這點,保證每一個malloc都有對應的free,每一個new都有對應的deleted!!!平時要養成這樣一個好的習慣。
要避免內存泄漏能夠總結爲如下幾點:
轉自吳秦