概述
內存泄漏(memory leak)指因爲疏忽或錯誤形成程序未能釋放已經再也不使用的內存的狀況,在大型的、複雜的應用程序中,內存泄漏是常見的問題。當之前分配的一片內存再也不須要使用或沒法訪問時,可是卻並無釋放它,這時就出現了內存泄漏。儘管優秀的編程實踐能夠確保最少的泄漏,可是根據經驗,當使用大量的函數對相同的內存塊進行處理時,極可能會出現內存泄漏。html
內存泄露能夠分爲如下幾類:
1. 常發性內存泄漏。發生內存泄漏的代碼會被屢次執行到,每次被執行的時候都會致使一塊內存泄漏。
2. 偶發性內存泄漏。發生內存泄漏的代碼只有在某些特定環境或操做過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶發性的也許就變成了常發性的。因此測試環境和測試方法對檢測內存泄漏相當重要。
3. 一次性內存泄漏。發生內存泄漏的代碼只會被執行一次,或者因爲算法上的缺陷,致使總會有一塊且僅一塊內存發生泄漏。好比,在一個Singleton類的構造函數中分配內存,在析構函數中卻沒有釋放該內存。而Singleton類只存在一個實例,因此內存泄漏只會發生一次。
4. 隱式內存泄漏。程序在運行過程當中不停的分配內存,可是直到結束的時候才釋放內存。嚴格的說這裏並無發生內存泄漏,由於最終程序釋放了全部申請的內存。可是對於一個服務器程序,須要運行幾天,幾周甚至幾個月,不及時釋放內存也可能致使最終耗盡系統的全部內存。因此,咱們稱這類內存泄漏爲隱式內存泄漏。linux
內存泄漏檢測工具
如今有不少方法來檢測內存泄露,如下列舉了linux經常使用的內存泄露檢測工具。算法
一、mtrace
應用環境:Linux GLIBC編程
編程語言:C數組
使用方法: 包含頭文件mcheck.h,定義環境變量MALLOC_TRACE爲輸出文件名,程序開始時調用mtrace()便可。服務器
結果輸出:用戶指定的文件數據結構
設計思路: 爲malloc,realloc,free函數添加鉤子函數,記錄每一對malloc-free的執行app
優缺點:只能檢查使用malloc/realloc/free形成的的內存泄露編程語言
如何獲取:GLIBC自帶,可直接使用函數
二、memwatch
應用環境:Linux
編程語言:C
使用方法: 加入memwatch.h,編譯時加上-DMEMWATCH -DMW_STDIO及memwatch.c
結果輸出:輸出文件名稱爲memwatch.log,在程序執行期間,錯誤提示都會顯示在stdout上
設計思路:將malloc/realloc/calloc/strdup/free等重定義爲mwMalloc(sz, __FILE__, __LINE__)等,內部維護一個操做鏈表
優缺點:能檢測雙重釋放(double-free)、錯誤釋放(erroneous free)、內存泄漏(unfreed memory)、溢出(Overflow)、下溢(Underflow)等等
如何獲取:http://memwatch.sourceforge.net/
三、valgrind
應用環境:Linux
編程語言:C/C++
使用方法: 編譯時加上-g選項,如 gcc -g filename.c -o filename,使用以下命令檢測內存使用狀況:
結果輸出:#valgrind --tool=memcheck --leak-check=yes --show-reachable=yes ./filename,就會看到內存使用報告
設計思路:根據軟件的內存操做維護一個有效地址空間表和無效地址空間表(進程的地址空間)
優缺點:可以檢測:
- 使用未初始化的內存 (Use of uninitialised memory)
- 使用已經釋放了的內存 (Reading/writing memory after it has been free’d)
- 使用超過 malloc分配的內存空間(Reading/writing off the end of malloc’d blocks)
- 對堆棧的非法訪問 (Reading/writing inappropriate areas on the stack)
- 申請的空間是否有釋放 (Memory leaks – where pointers to malloc’d blocks are lost forever)
- malloc/free/new/delete申請和釋放內存的匹配(Mismatched use of malloc/new/new [] vs free/delete/delete [])
- src和dst的重疊(Overlapping src and dst pointers in memcpy() and related functions)
- 重複free
如何獲取:http://valgrind.org/
四、debug_new
應用環境:Linux/Windows
編程語言:C++
使用方法: 包含頭文件debug_new.h,連接debug_new.cpp
結果輸出:控制檯console
設計思路: 經過重載new和delete操做符來捕獲內存申請/釋放請求,並在程序內部維護一個全局靜態變量的哈希鏈表。在new操做符中,不只僅分配用戶所要求的內存,而是在爲每次分配的內存都添加一個頭部,存儲着這次分配的位置信息和鏈表指針,new返回的是分配的這塊內存加上頭部偏移後的值,而在以前已經將此返回值做了HASH計算並添加到HASH鏈表中了。delete的時候先根據要釋放的指針地址作HASH計算,而後再遍歷數組HASH值處的鏈表進行查找,若是找到則將該節點移除,未找到就abort。這樣在程序結束以後,經過檢查此數組中是否還有未釋放的內存塊來肯定是否有內存泄露。
優缺點:跨平臺,僅用於C++程序,
如何獲取:http://www.ibm.com/developerworks/cn/linux/l-mleak2/index.html
總結
以上的這些分析工具,所使用的方法大體分爲如下幾種:
一、註冊內存分配/釋放鉤子函數(hook)。在Linux下能夠malloc_hook, free_hook等5個鉤子函數,在Windows下能夠註冊_CrtSetAllocHook鉤子函數,這樣在分配內存的時候就能夠捕獲這一請求並加以處理。Visual Leak Detecter和mtrace使用此方式。
二、使用宏定義替換。將用戶代碼中的malloc, free 替換爲宏定義的 mwMalloc(sz, __FILE__, __LINE__)等自定義函數,從而跟蹤內存請求,memwatch即便用此方式。
三、操做符重載。此方法僅用於C++語言中,經過重載new、delete操做符來實現跟蹤內存請求,重載後的操做符相似於鉤子函數意義。debug_new採用此方式。
這些工具的輸出方式也分如下幾種:
一、普通環境下通常輸出到調試窗口中,不少軟件自己就提供了一個理想的輸出場所,而且GUI應用程序輸出到標準輸出時不可見的。Visual Leak Detecter採用此法。
二、輸出到標準輸出或標準錯誤輸出:控制檯應用程序能夠輸出到屏幕,如memwatch, valgrind, debug_new都是採用這種方法。
三、輸出到日誌文件:將結果輸出到用戶指定或默認的日誌文件中,如mtrace和memwatch。
此外,這些工具的內存檢測方式無非也分爲兩種:
一、維護一個內存操做鏈表,當有內存申請操做時,將其加入此鏈表中,當有釋放操做時,從申請操做從鏈表中移除。若是到程序結束後此鏈表中還有內容,說明有內存泄露了;若是要釋放的內存操做沒有在鏈表中找到對應操做,則說明是釋放了屢次。使用此方法的有內置的調試工具,Visual Leak Detecter,mtrace, memwatch, debug_new。
二、模擬進程的地址空間。仿照操做系統對進程內存操做的處理,在用戶態下維護一個地址空間映射,此方法要求對進程地址空間的處理有較深的理解。由於Windows的進程地址空間分佈不是開源的,因此模擬起來很困難,所以只支持Linux。採用此方法的是valgrind。