自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

在 Linux下調(diào)試內(nèi)存泄漏的方法

運維 系統(tǒng)運維
該如何發(fā)現(xiàn)你C和C++內(nèi)存存在問題呢?當運行速度比較慢的時候是怎么回事兒呢?此時,你該考慮的問題是內(nèi)存泄露。本文闡明了如何發(fā)現(xiàn)內(nèi)存泄露、分析和檢測內(nèi)存泄露的全過程。

  由于內(nèi)存泄漏不是顯而易見,而且存在內(nèi)存錯誤的 C 和 C++ 程序會導致各種問題,所以需要特別關注 C 和 C++ 編程的內(nèi)存問題,特別是內(nèi)存泄漏。本文先從如何發(fā)現(xiàn)內(nèi)存泄漏,然后是用不同的方法和工具定位內(nèi)存泄漏,最后對這些工具進行了比較,另外還簡單介紹了資源泄漏的處理(以句柄泄漏為例)。本文使用的測試平臺是:Linux (Redhat AS4)。但是這些方法和工具許多都不只是局限于 C/C++ 語言以及 linux 操作系統(tǒng)。

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

1. 如何發(fā)現(xiàn)內(nèi)存泄漏

  有些簡單的內(nèi)存泄漏問題可以從在代碼的檢查階段確定。還有些泄漏比較嚴重的,即在很短的時間內(nèi)導致程序或系統(tǒng)崩潰,或者系統(tǒng)報告沒有足夠內(nèi)存,也比較容易發(fā)現(xiàn)。最困難的就是泄漏比較緩慢,需要觀測幾天、幾周甚至幾個月才能看到明顯異常現(xiàn)象。那么如何在比較短的時間內(nèi)檢測出有沒有潛在的內(nèi)存泄漏問題呢?實際上不同的系統(tǒng)都帶有內(nèi)存監(jiān)視工具,我們可以從監(jiān)視工具收集一段時間內(nèi)的堆棧內(nèi)存信息,觀測增長趨勢,來確定是否有內(nèi)存泄漏。在 Linux 平臺可以用 ps 命令,來監(jiān)視內(nèi)存的使用,比如下面的命令 (觀測指定進程的VSZ值):

  ps -aux

2. 靜態(tài)分析

  包括手動檢測和靜態(tài)工具分析,這是代價最小的調(diào)試方法。

(1)手動檢測

  當使用 C/C++ 進行開發(fā)時,采用良好的一致的編程規(guī)范是防止內(nèi)存問題第一道也是最重要的措施。檢測是編碼標準的補充。二者各有裨益,但結合使用效果特別好。專業(yè)的 C 或 C++ 專業(yè)人員甚至可以瀏覽不熟悉的源代碼,并以極低的成本檢測內(nèi)存問題。通過少量的實踐和適當?shù)奈谋舅阉?,您能夠快速驗證平衡的 *alloc() 和 free() 或者 new 和 delete 的源主體。人工查看此類內(nèi)容通常會出現(xiàn)像清單 1 中一樣的問題,可以定位出在函數(shù) LeakTest 中的堆變量 Logmsg 沒有釋放。

  清單1. 簡單的內(nèi)存泄漏

  1.   #include   
  2.  
  3.   #include   
  4.  
  5.   #include   
  6.  
  7.   int LeakTest(char * Para)  
  8.  
  9.   {  
  10.  
  11.   if(NULL==Para){  
  12.  
  13.   //local_log("LeakTest Func: empty parameter\n");  
  14.  
  15.   return -1;  
  16.  
  17.   }  
  18.  
  19.   char * Logmsg = new char[128];  
  20.  
  21.   if(NULL == Logmsg){  
  22.  
  23.   //local_log("memeory allocation failed\n");  
  24.  
  25.   return -2;  
  26.  
  27.   }  
  28.  
  29.   sprintf(Logmsg,"LeakTest routine exit: '%s'.\n", Para);  
  30.  
  31.   //local_log(Logmsg);  
  32.  
  33.   return 0;  
  34.  
  35.   }  
  36.  
  37.   int main(int argc,char **argv )  
  38.  
  39.   {  
  40.  
  41.   char szInit [] = "testcase1";  
  42.  
  43.   LeakTest(szInit);  
  44.  
  45.   return 0;  
  46.  
  47.   }  
  48.  

(2)靜態(tài)代碼分析工具

  代碼靜態(tài)掃描和分析的工具比較多,比如 splint, PC-LINT, BEAM 等。因為 BEAM 支持的平臺比較多,這以 BEAM 為例,做個簡單介紹,其它有類似的處理過程。

  BEAM 可以檢測四類問題: 沒有初始化的變量;廢棄的空指針;內(nèi)存泄漏;冗余計算。而且支持的平臺比較多。

  BEAM 支持以下平臺:

  Linux x86 (glibc 2.2.4)

  Linux s390/s390x (glibc 2.3.3 or higher)

  Linux (PowerPC, USS) (glibc 2.3.2 or higher)

  AIX (4.3.2+)

  Window2000 以上

  清單2. 用作 Beam 分析的代碼

  1.   #include   
  2.  
  3.   #include   
  4.  
  5.   #include   
  6.  
  7.   int *p;  
  8.  
  9.   void  
  10.  
  11.   foo(int a)  
  12.  
  13.   {  
  14.  
  15.   int b, c;  
  16.  
  17.   b = 0;  
  18.  
  19.   if(!p)  
  20.  
  21.   c = 1;  
  22.  
  23.   if(c > a)  
  24.  
  25.   c += p[1];  
  26.  
  27.   }  
  28.  
  29.   int LeakTest(char * Para)  
  30.  
  31.   {  
  32.  
  33.   char * Logmsg = new char[128];  
  34.  
  35.   if((Para==NULL)||(Logmsg == NULL))  
  36.  
  37.   return -1;  
  38.  
  39.   sprintf(Logmsg,"LeakTest routine exit: '%s'.\n", Para);  
  40.  
  41.   return 0;  
  42.  
  43.   }  
  44.  
  45.   int main(int argc,char **argv )  
  46.  
  47.   {  
  48.  
  49.   char szInit [] = "testcase1";  
  50.  
  51.   LeakTest(szInit);  
  52.  
  53.   return 0;  
  54.  
  55.   }  
  56.  

  下面以 X86 Linux 為例,代碼如清單 2,具體的環(huán)境如下:

  OS: Red Hat Enterprise Linux AS release 4 (Nahant Update 2)

  GCC: gcc version 3.4.4

  BEAM: 3.4.2; https://w3.eda.ibm.com/beam/

  可以把 BEAM 看作一個 C/C++ 編譯器,按下面的命令進行編譯 (前面兩個命令是設置編譯器環(huán)境變量):

 

  1.   ./beam-3.4.2/bin/beam_configure --c gcc  
  2.  
  3.   ./beam-3.4.2/bin/beam_configure --cpp g++  
  4.  
  5.   ./beam-3.4.2/bin/beam_compile --beam::compiler=compiler_cpp_config.tcl -cpp code2.cpp  
  6.  

  從下面的編譯報告中,我們可以看到這段程序中有三個錯誤:”內(nèi)存泄漏”;“變量未初始化”;“ 空指針操作”

  1.   "code2.cpp", line 10: warning: variable "b" was set but never used  
  2.  
  3.   int b, c;  
  4.  
  5.   ^  
  6.  
  7.   BEAM_VERSION=3.4.2  
  8.  
  9.   BEAM_ROOT=/home/hanzb/memdetect  
  10.  
  11.   BEAM_DIRECTORY_WRITE_INNOCENTS=  
  12.  
  13.   BEAM_DIRECTORY_WRITE_ERRORS=  
  14.  
  15.   -- ERROR23(heap_memory) /*memory leak*/ >>>ERROR23_LeakTest_7b00071dc5cbb458  
  16.  
  17.   "code2.cpp", line 24: memory leak  
  18.  
  19.   ONE POSSIBLE PATH LEADING TO THE ERROR:  
  20.  
  21.   "code2.cpp", line 22: allocating using `operator new[]' (this memory will not be freed)  
  22.  
  23.   "code2.cpp", line 22: assigning into `Logmsg'  
  24.  
  25.   "code2.cpp", line 24: deallocating `Logmsg' because exiting its scope  
  26.  
  27.   (losing last pointer to the memory)  
  28.  
  29.   -- ERROR1 /*uninitialized*/ >>>ERROR1_foo_60c7889b2b608  
  30.  
  31.   "code2.cpp", line 16: uninitialized `c'  
  32.  
  33.   ONE POSSIBLE PATH LEADING TO THE ERROR:  
  34.  
  35.   "code2.cpp", line 10: allocating `c'  
  36.  
  37.   "code2.cpp", line 13: the if-condition is false  
  38.  
  39.   "code2.cpp", line 16: getting the value of `c'  
  40.  
  41.   VALUES AT THE END OF THE PATH:  
  42.  
  43.   p != 0  
  44.  
  45.   -- ERROR2 /*operating on NULL*/ >>>ERROR2_foo_af57809a2b615  
  46.  
  47.   "code2.cpp", line 17: invalid operation involving NULL pointer  
  48.  
  49.   ONE POSSIBLE PATH LEADING TO THE ERROR:  
  50.  
  51.   "code2.cpp", line 13: the if-condition is true (used as evidence that error is possible)  
  52.  
  53.   "code2.cpp", line 16: the if-condition is true  
  54.  
  55.   "code2.cpp", line 17: invalid operation `[]' involving NULL pointer `p'  
  56.  
  57.   VALUES AT THE END OF THE PATH:  
  58.  
  59.   c = 1 
  60.  
  61.   p = 0 
  62.  
  63.   a <= 0  
  64.  

#p#

(3) 內(nèi)嵌程序

  可以重載內(nèi)存分配和釋放函數(shù) new 和 delete,然后編寫程序定期統(tǒng)計內(nèi)存的分配和釋放,從中找出可能的內(nèi)存泄漏。或者調(diào)用系統(tǒng)函數(shù)定期監(jiān)視程序堆的大小,關鍵要確定堆的增長是泄漏而不是合理的內(nèi)存使用。這類方法比較復雜,在這就不給出詳細例子了。

3. 動態(tài)運行檢測

  實時檢測工具主要有 valgrind, Rational purify 等。

(1) Valgrind

  valgrind 是幫助程序員尋找程序里的 bug 和改進程序性能的工具。程序通過 valgrind 運行時,valgrind 收集各種有用的信息,通過這些信息可以找到程序中潛在的 bug 和性能瓶頸。

  Valgrind 現(xiàn)在提供多個工具,其中最重要的是 Memcheck,Cachegrind,Massif 和 Callgrind。Valgrind 是在 Linux 系統(tǒng)下開發(fā)應用程序時用于調(diào)試內(nèi)存問題的工具。它尤其擅長發(fā)現(xiàn)內(nèi)存管理的問題,它可以檢查程序運行時的內(nèi)存泄漏問題。其中的 memecheck 工具可以用來尋找 c、c++ 程序中內(nèi)存管理的錯誤。可以檢查出下列幾種內(nèi)存操作上的錯誤:

  讀寫已經(jīng)釋放的內(nèi)存

  讀寫內(nèi)存塊越界(從前或者從后)

  使用還未初始化的變量

  將無意義的參數(shù)傳遞給系統(tǒng)調(diào)用

  內(nèi)存泄漏

(2) Rational purify

  Rational Purify 主要針對軟件開發(fā)過程中難于發(fā)現(xiàn)的內(nèi)存錯誤、運行時錯誤。在軟件開發(fā)過程中自動地發(fā)現(xiàn)錯誤,準確地定位錯誤,提供完備的錯誤信息,從而減少了調(diào)試時間。同時也是市場上唯一支持多種平臺的類似工具,并且可以和很多主流開發(fā)工具集成。Purify 可以檢查應用的每一個模塊,甚至可以查出復雜的多線程或進程應用中的錯誤。另外不僅可以檢查 C/C++,還可以對 Java 或 .NET 中的內(nèi)存泄漏問題給出報告。

  在 Linux 系統(tǒng)中,使用 Purify 需要重新編譯程序。通常的做法是修改 Makefile 中的編譯器變量。下面是用來編譯本文中程序的 Makefile:

  CC=purify gcc

  首先運行 Purify 安裝目錄下的 purifyplus_setup.sh 來設置環(huán)境變量,然后運行 make 重新編譯程序。

  ./purifyplus_setup.sh

  下面給出編譯一個代碼文件的示例,源代碼文件命名為 test3.cpp. 用 purify 和 g++ 的編譯命令如下,‘-g’是編譯時加上調(diào)試信息。

  purify g++ -g test3.cpp –o test

  運行編譯生成的可執(zhí)行文件 test,就可以得到圖1,可以定位出內(nèi)存泄漏的具體位置。

  ./test

  清單3. Purify 分析的代碼

  1.   #include   
  2.  
  3.   char * Logmsg;  
  4.  
  5.   int LeakTest(char * Para)  
  6.  
  7.   {  
  8.  
  9.   if(NULL==Para){  
  10.  
  11.   //local_log("LeakTest Func: empty parameter\n");  
  12.  
  13.   return -1;  
  14.  
  15.   }  
  16.  
  17.   Logmsg = new char[128];  
  18.  
  19.   for (int i = 0 ; i < 128; i++)  
  20.  
  21.   Logmsg[i] = i%64;  
  22.  
  23.   if(NULL == Logmsg){  
  24.  
  25.   //local_log("memeory allocation failed\n");  
  26.  
  27.   return -2;  
  28.  
  29.   }  
  30.  
  31.   sprintf(Logmsg,"LeakTest routine exit: '%s'.\n", Para);  
  32.  
  33.   //local_log(Logmsg);  
  34.  
  35.   return 0;  
  36.  
  37.   }  
  38.  
  39.   int main(int argc,char **argv )  
  40.  
  41.   {  
  42.  
  43.   char szInit [] = "testcase1";  
  44.  
  45.   int i;  
  46.  
  47.   LeakTest(szInit);  
  48.  
  49.   for (i=0; i < 2; i++){  
  50.  
  51.   if(i%200 == 0)  
  52.  
  53.   LeakTest(szInit);  
  54.  
  55.   sleep(1);  
  56.  
  57.   }  
  58.  
  59.   return 0;  
  60.  
  61.   } 

  需要指出的是,程序必須編譯成調(diào)試版本才可以定位到具體哪行代碼發(fā)生了內(nèi)存泄漏。即在 gcc 或者 g++ 中,必須使用 "-g" 選項。

  圖 1 purify 的輸出結果

 

Linux/內(nèi)存泄露 

        以上就是幾種內(nèi)存泄露,以及調(diào)試方法。對程序內(nèi)存泄露的問題有著一定的幫助。

【編輯推薦】

  1. 在iPhone應用中如何避免內(nèi)存泄露
  2. Linux 內(nèi)存監(jiān)控內(nèi)存泄露和回收內(nèi)存的方法
  3. Windows 7被曝內(nèi)存泄露缺陷 可導致系統(tǒng)崩潰
  4. Linux kernel多個內(nèi)存泄露本地拒絕服務漏洞
  5. Linux Kernel 2.4 RTC處理函數(shù)內(nèi)存泄露漏洞
  6. Linux內(nèi)核本地整數(shù)溢出和內(nèi)存泄露漏洞

 

責任編輯:zhaolei 來源: 網(wǎng)絡轉載
相關推薦

2021-11-05 08:28:27

內(nèi)存泄漏調(diào)試

2010-08-18 16:18:59

IE6CSS

2011-04-19 13:07:27

LinuxC語言

2018-12-07 10:52:08

內(nèi)存泄漏方法

2010-09-25 11:07:45

Java內(nèi)存泄漏

2024-01-30 10:12:00

Java內(nèi)存泄漏

2018-02-01 17:32:30

LinuxUNIXBash Shell

2021-04-20 09:52:43

Linuxcore dump代碼

2018-09-14 10:48:45

Java內(nèi)存泄漏

2015-03-30 11:18:50

內(nèi)存管理Android

2019-01-30 18:24:14

Java內(nèi)存泄漏編程語言

2022-10-31 15:34:30

python裝飾器內(nèi)存泄漏

2021-11-08 12:44:48

AndroidC++內(nèi)存

2011-09-07 14:31:59

ubuntu屏幕亮度

2024-03-11 08:22:40

Java內(nèi)存泄漏

2023-12-18 10:45:23

內(nèi)存泄漏計算機服務器

2009-06-16 11:17:49

內(nèi)存泄漏

2012-06-19 15:12:20

Java內(nèi)存泄露

2024-02-21 08:00:55

WindowsDWM進程

2012-02-22 21:28:58

內(nèi)存泄漏
點贊
收藏

51CTO技術棧公眾號