天天看點

C/C++記憶體洩漏及檢測1、記憶體洩漏簡介及後果2、Windows平台下的記憶體洩漏檢測3、Linux平台下的記憶體洩漏檢測4、總結

“該死系統存在記憶體洩漏問題”,項目中由于各方面因素,總是有人抱怨存在記憶體洩漏,系統長時間運作之後,可用記憶體越來越少,甚至導緻了某些服務失

敗。記憶體洩漏是最難發現的常見錯誤之一,因為除非用完記憶體或調用malloc失敗,否則都不會導緻任何問題。實際上,使用c/c++這類沒有垃圾回收機制

的語言時,你很多時間都花在處理如何正确釋放記憶體上。如果程式運作時間足夠長,如背景程序運作在伺服器上,隻要伺服器不當機就一直運作,一個小小的失誤也

會對程式造成重大的影響,如造成某些關鍵服務失敗。

對于記憶體洩漏,本人深有體會!實習的時候,公司一個項目中就存在記憶體洩漏問題,項目的代碼兩非常大,背景程序也比較多,造成記憶體洩漏的地方比較難

找。這次機會是我對如何查找記憶體洩漏問題,有了一定的經驗,後面自己的做了相關實驗,在此我分享一下記憶體洩漏如何調試查找,主要内容如下:

1、記憶體洩漏簡介

2、windows平台下的記憶體洩漏檢測

2.1、檢測是否存在記憶體洩漏問題

2.2、定位具體的記憶體洩漏地方

3、linux平台下的記憶體洩漏檢測 

4、總結

其實windows、linux下面的記憶體檢測都可以單獨開篇詳細介紹,方法和工具也遠遠不止文中介紹到的,我的方法也不是最優的,如果您有更好的方法,也請您告訴我和大家。

wikipedia中這樣定義記憶體洩漏:在計算機科學中,記憶體洩漏指由于疏忽或錯誤造成程式未能釋放已經不再使用的記憶體的情況。記憶體洩漏并非指記憶體

在實體上的消失,而是應用程式配置設定某段記憶體後,由于設計錯誤,導緻在釋放該段記憶體之前就失去了對該段記憶體的控制,進而造成了記憶體的浪費。

最難捉摸也最難檢測到的錯誤之一是記憶體洩漏,即未能正确釋放以前配置設定的記憶體的 bug。

隻發生一次的小的記憶體洩漏可能不會被注意,但洩漏大量記憶體的程式或洩漏日益增多的程式可能會表現出各種征兆:從性能不良(并且逐漸降低)到記憶體完全用盡。

更糟的是,洩漏的程式可能會用掉太多記憶體,以緻另一個程式失敗,而使使用者無從查找問題的真正根源。 此外,即使無害的記憶體洩漏也可能是其他問題的征兆。

記憶體洩漏會因為減少可用記憶體的數量進而降低計算機的性能。最終,在最糟糕的情況下,過多的可用記憶體被配置設定掉導緻全部或部分裝置停止正常工作,或者應

用程式崩潰。記憶體洩漏可能不嚴重,甚至能夠被正常的手段檢測出來。在現代作業系統中,一個應用程式使用的正常記憶體在程式終止時被釋放。這表示一個短暫運作

的應用程式中的記憶體洩漏不會導緻嚴重後果。

在以下情況,記憶體洩漏導緻較嚴重的後果:

新的記憶體被頻繁地配置設定,比如當顯示電腦遊戲或動畫視訊畫面時;

洩漏在作業系統内部發生;

洩漏在系統關鍵驅動中發生;

下面我們通過以下例子來介紹如何檢測記憶體洩漏問題:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

<code>#include &lt;stdlib.h&gt;</code>

<code>#include &lt;iostream&gt;</code>

<code>using</code> <code>namespace</code> <code>std;</code>

<code>void</code> <code>getmemory(</code><code>char</code> <code>*p,</code><code>int</code> <code>num)</code>

<code>{</code>

<code>    </code><code>p = (</code><code>char</code><code>*)</code><code>malloc</code><code>(</code><code>sizeof</code><code>(</code><code>char</code><code>) * num);</code><code>//使用new也能夠檢測出來</code>

<code>}</code>

<code>int</code> <code>main(</code><code>int</code> <code>argc,</code><code>char</code><code>** argv)</code>

<code>    </code><code>char</code> <code>*str = null;</code>

<code>    </code><code>getmemory(str, 100);</code>

<code>    </code><code>cout&lt;&lt;</code><code>"memory leak test!"</code><code>&lt;&lt;endl;</code>

<code>    </code><code>//如果main中存在while循環調用getmemory</code>

<code>    </code><code>//那麼問題将變得很嚴重</code>

<code>    </code><code>//while(1){getmemory(...);}</code>

<code>    </code><code>return</code> <code>0;</code>

實際中不可能這麼簡單,如果這麼簡單也用不着别的方法,程式員一眼就可以看出問題,此程式隻用于測試。

windows平台下面visual studio 調試器和 c 運作時 (crt)

庫為我們提供了檢測和識别記憶體洩漏的有效方法,原理大緻如下:記憶體配置設定要通過crt在運作時實作,隻要在配置設定記憶體和釋放記憶體時分别做好記錄,程式結束時對

比配置設定記憶體和釋放記憶體的記錄就可以确定是不是有記憶體洩漏。在vs中啟用記憶體檢測的方法如下:

step1,在程式中包括以下語句: (#include 語句必須采用上文所示順序。 如果更改了順序,所使用的函數可能無法正常工作。)

<code>#define _crtdbg_map_alloc</code>

<code>#include &lt;crtdbg.h&gt;</code>

#define 語句将 crt 堆函數的基版本映射到對應的“debug”版本。 并非絕對需要該語句;但如果沒有該語句,記憶體洩漏轉儲包含的有用資訊将較少。

step2, 在添加了上述語句之後,可以通過在程式中包括以下語句(通常應恰好放在程式退出位置之前)來轉儲記憶體洩漏資訊:

<code>_crtdumpmemoryleaks();</code>

此時,完整的代碼如下:

<a href="http://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html#">+ view code</a>

C/C++記憶體洩漏及檢測1、記憶體洩漏簡介及後果2、Windows平台下的記憶體洩漏檢測3、Linux平台下的記憶體洩漏檢測4、總結

如果沒有使用 #define _crtdbg_map_alloc 語句,記憶體洩漏轉儲将如下所示:

C/C++記憶體洩漏及檢測1、記憶體洩漏簡介及後果2、Windows平台下的記憶體洩漏檢測3、Linux平台下的記憶體洩漏檢測4、總結

未定義 _crtdbg_map_alloc 時,所顯示的會是:

記憶體配置設定編号(在大括号内)。

“普通塊”是由程式配置設定的普通記憶體。 “用戶端塊”是由 mfc 程式用于需要析構函數的對象的特殊類型記憶體塊。 mfc new 操作根據正在建立的對象的需要建立普通塊或用戶端塊。 “crt 塊”是由 crt 庫為自己使用而配置設定的記憶體塊。 crt 庫處理這些塊的釋放,是以您不大可能在記憶體洩漏報告中看到這些塊,除非出現嚴重錯誤(例如 crt 庫損壞)。 從不會在記憶體洩漏資訊中看到下面兩種塊類型: “可用塊”是已釋放的記憶體塊。 “忽略塊”是您已特别标記的塊,因而不出現在記憶體洩漏報告中。

十六進制形式的記憶體位置。

以位元組為機關的塊大小。

前 16 位元組的内容(亦為十六進制)。

定義了 _crtdbg_map_alloc 時,還會顯示在其中配置設定洩漏的記憶體的檔案。 檔案名後括号中的數字(本示例中為 10)是該檔案中的行号。

該語句在程式退出時自動調用 _crtdumpmemoryleaks。 必須同時設定 _crtdbg_alloc_mem_df 和 _crtdbg_leak_check_df 兩個位域,如前面所示。

通過上面的方法,我們幾乎可以定位到是哪個地方調用記憶體配置設定函數malloc和new等,如上例中的getmemory函數中,即第10行!但是不

能定位到,在哪個地方調用getmemory()導緻的記憶體洩漏,而且在大型項目中可能有很多處調用getmemory。如何要定位到在哪個地方調用

getmemory導緻的記憶體洩漏?

定位記憶體洩漏的另一種技術涉及在關鍵點對應用程式的記憶體狀态拍快照。 crt 庫提供一種結構類型 _crtmemstate,您可用它存儲記憶體狀态的快照:

<code>_crtmemstate s1, s2, s3;</code>

<code>_crtmemcheckpoint( &amp;s1 );</code>

<code>_crtmemdumpstatistics( &amp;s1 );</code>

<code>// memory allocations take place here</code>

<code>_crtmemcheckpoint( &amp;s2 );</code>

<code>if</code> <code>( _crtmemdifference( &amp;s3, &amp;s1, &amp;s2) )</code>

<code>   </code><code>_crtmemdumpstatistics( &amp;s3 );</code>

顧名思義,_crtmemdifference 比較兩個記憶體狀态(s1 和 s2),生成這兩個狀态之間差異的結果(s3)。 在程式的開始和結尾放置 _crtmemcheckpoint 調用,并使用_crtmemdifference 比較結果,是檢查記憶體洩漏的另一種方法。 如果檢測到洩漏,則可以使用 _crtmemcheckpoint 調用通過二進制搜尋技術來劃分程式和定位洩漏。

如上面的例子程式我們可以這樣來定位确切的調用getmemory的地方:

調試時,程式輸出如下結果:

C/C++記憶體洩漏及檢測1、記憶體洩漏簡介及後果2、Windows平台下的記憶體洩漏檢測3、Linux平台下的記憶體洩漏檢測4、總結

這說明在s1和s2之間存在記憶體洩漏!!!如果getmemory不是在s1和s2之間調用,那麼就不會有資訊輸出。

C/C++記憶體洩漏及檢測1、記憶體洩漏簡介及後果2、Windows平台下的記憶體洩漏檢測3、Linux平台下的記憶體洩漏檢測4、總結

如上圖所示知道:

==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)

其實記憶體洩漏的原因可以概括為:調用了malloc/new等記憶體申請的操作,但缺少了對應的free/delete,總之就是,malloc

/new比free/delete的數量多。我們在程式設計時需要注意這點,保證每個malloc都有對應的free,每個new都有對應的

deleted!!!平時要養成這樣一個好的習慣。

要避免記憶體洩漏可以總結為以下幾點:

程式員要養成良好習慣,保證malloc/new和free/delete比對;

檢測記憶體洩漏的關鍵原理就是,檢查malloc/new和free/delete是否比對,一些工具也就是這個原理。要做到這點,就是利用宏或者鈎子,在使用者程式與運作庫之間加了一層,用于記錄記憶體配置設定情況。

繼續閱讀