1. 程式人生 > >Linux中定位記憶體洩漏

Linux中定位記憶體洩漏

1. 什麼是記憶體洩漏

記憶體洩漏是指堆記憶體的洩漏。堆記憶體是指程式從堆中分配的、大小任意的(記憶體塊的大小可以在程式執行期決定)、使用完後必須顯示釋放的記憶體。應用程式一般使用malloc、realloc、new等函式從堆中分配到一塊記憶體,使用完後,程式必須負責相應的呼叫free或delete釋放該記憶體塊。否則,這塊記憶體就不能被再次使用,造成這塊記憶體洩漏。

2. 記憶體洩漏的檢測

C++程式缺乏相應的手段來檢測記憶體資訊,只能使用top指令觀察程序的動態記憶體總額。而且程式退出時,我們無法獲知任何記憶體洩漏資訊。

使用Linux命令回收記憶體:可以使用ps、kill兩個命令檢測記憶體使用情況和進行回收。
在使用超級使用者許可權時使用命令“ps”,它會列出所有正在執行的程式名稱和對應的程序號(PID)。
kill命令的工作原理是向Linux作業系統的核心送出一個系統操作訊號和程式的程序號(PID)。

3. Valgrind

3.1 Valgrind體系結構

Valgrind是一套Linux下,開源的模擬除錯工具的集合。Valgrind由核心(core)以及基於核心的其他除錯工具組成。核心類似於一個框架(framework),它模擬了一個CPU環境,並提供服務給其他工具;而其他工具則類似於外掛 (plug-in),利用核心提供的服務完成各種特定的記憶體除錯任務。Valgrind的體系結構如下圖所示:
這裡寫圖片描述

Valgrind包括如下一些工具:
1. Memcheck。這是valgrind應用最廣泛的工具,一個重量級的記憶體檢查器,能夠發現開發中絕大多數記憶體錯誤使用情況,比如:使用未初始化的記憶體,使用已經釋放了的記憶體,記憶體訪問越界等。這也是本文將重點介紹的部分。
2. Callgrind。它主要用來檢查程式中函式呼叫過程中出現的問題。
3. Cachegrind。它主要用來檢查程式中快取使用出現的問題。
4. Helgrind。它主要用來檢查多執行緒程式中出現的競爭問題。
5. Massif。它主要用來檢查程式中堆疊使用中出現的問題。
6. Extension。可以利用core提供的功能,自己編寫特定的記憶體除錯工具。

3.2 Valgrind.Memcheck 檢測記憶體原理

這裡寫圖片描述

Memcheck 能夠檢測出記憶體問題,關鍵在於其建立了兩個全域性表。

1. Valid-Value 表:

對於程序的整個地址空間中的每一個位元組(byte),都有與之對應的 8 個 bits;對於 CPU 的每個暫存器,也有一個與之對應的 bit 向量。這些 bits 負責記錄該位元組或者暫存器值是否具有有效的、已初始化的值。

2. Valid-Address 表

對於程序整個地址空間中的每一個位元組(byte),還有與之對應的 1 個 bit,負責記錄該地址是否能夠被讀寫。

檢測原理:

當要讀寫記憶體中某個位元組時,首先檢查這個位元組對應的 A bit。如果該A bit顯示該位置是無效位置,memcheck 則報告讀寫錯誤。
核心(core)類似於一個虛擬的 CPU 環境,這樣當記憶體中的某個位元組被載入到真實的 CPU 中時,該位元組對應的 V bit 也被載入到虛擬的 CPU 環境中。一旦暫存器中的值,被用來產生記憶體地址,或者該值能夠影響程式輸出,則 memcheck 會檢查對應的V bits,如果該值尚未初始化,則會報告使用未初始化記憶體錯誤。

3.3 Valgrind使用

第一步 準備可執行程式

1、為了使valgrind發現的錯誤更精確,能夠定位到原始碼行,建議在編譯時加上-g引數。
2、編譯優化選項請選擇O0,雖然這會降低程式的執行效率。

示例程式檔名為:sample.c, 選用的編譯器為gcc。
生成可執行程式

gcc –g –O0 sample.c –o sample

第二步 在valgrind下,執行可執行程式。

valgrind <path>/sample

第三步:分析valgrind的輸出資訊。

3.4 Memcheck發現的常見記憶體問題

1、使用未初始化的記憶體
2、記憶體讀寫越界
3、記憶體覆蓋
4、動態記憶體管理錯誤
5、記憶體洩漏