前段時間,客戶現場的一臺服務器上跑的應用佔用內存不停的增加,最後把系統內存全部耗完,被系統kill掉了,查看日誌報out of memory。於是火急火燎的開始分析內存泄露的可能,差不多一個月左右的時間,都在上面耗着,一直找不到內存泄露的地方。
雖然還沒有找到內存泄露的具體原因,但是在網上找到了一個好的內存泄露分析工具,特做記錄。
一、 安裝
1. autoconf
#
wget http://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.gz
# tar -zxvf autoconf-2.69.tar.gz
# cd autoconf-2.69
# ./configure
#
make; make install
2.
automake
# wget
http://ftp.gnu.org/gnu/automake/automake-1.14.tar.gz
# tar -zxvf automake-1.14.tar.gz
# cd automake-1.14
# ./bootstrap.sh
# ./configure
#
make; make install
3.
valgrind
# wget
http://valgrind.org/downloads/valgrind-3.9.0.tar.bz2
# tar -jxvf valgrind-3.9.0.tar.bz2
# cd valgrind-3.9.0
# ./autogen.sh
#
./configure
# make; make
install
二、快速使用指南
1. 簡介
Valgrind是一款用於內存調試、內存泄漏檢測以及性能分析的軟件工具套裝。
它最流行的工具是Memcheck,
它能檢測C/C++中大部分的內存相關的錯誤。
2.
準備要檢查的程序
程序編譯時使用
“-g”參數,以添加調試信息,這樣Memcheck的錯誤消息可以精確到行;
編譯時使用“-O0”也有必要,只是速度會很慢,“-O1”可能會導致Memecheck的錯誤消息不正確;
3. 在Memcheck下運行程序:
如果你的程序的運行命令如下:
myprog arg1 arg2
則使用如下命令行:
valgrind --leak-check=yes
myprog arg1 arg2
Memcheck是valgrind默認的工具,"--leak-check"選項開啓了詳細內存泄漏檢測器;
這時程序會比平時運行得慢很多(如,慢20~30倍),並且會消耗更多的內存;
程序運行結束後,或你用“CTRL+C”中止程序後,Memcheck將會列出檢測到的內存出錯和泄漏的信息;
4. Memcheck輸出信息示例說明
下面是一個很簡單的示例C程序,並帶有一個內存錯誤和一個內存泄漏;
文件名爲:a.c
1
#include <stdlib.h>
2
3 void
f(void)
4 {
5 int* x = malloc(10 *
sizeof(int));
6 x[10] = 0;
// problem 1: heap block
overrun
7 }
// problem 2: memory leak -- x not
freed
8
9 int main(void)
10 {
11
f();
12 return 0;
13 }
Most
error messages look like the following, which describes problem 1, the heap
block overrun:
錯誤消息如下,描述了問題1,
內存寫越界
==19182== Invalid write of size
4
==19182== at 0x804838F: f
(example.c:6)
==19182== by 0x80483AB:
main (example.c:11)
==19182== Address
0x1BA45050 is 0 bytes after a block of size 40 alloc’d
==19182== at 0x1B8FF5CD: malloc
(vg_replace_malloc.c:130)
==19182== by
0x8048385: f (example.c:5)
==19182== by
0x80483AB: main (example.c:11)
需要注意的信息有:
.
每個錯誤都會有多行信息說明,需要仔細閱讀.
. 19182是進程ID,
通常不重要;
. 第一行("Invalid
write..."),說明了是哪種類型的錯誤;
在這裏,是程序寫越界了
.
第一行之下的行都是函數調用棧跟蹤,說明了問題的發生的地方;
函數調用棧可以很大,如果還使用了C++ STL時,更容易使人混亂;從底向上讀有助於理解;
如果函數調用棧不夠大,可以使用--num-callers選項來擴大;
. 代碼地址(eg.
0x804838F)通常不用關心,有時只是在跟蹤怪異的bug時有用;
.
有些錯誤信息有第二個組成部分,包括內存地址的描述等;
在這個例子中,這部分的信息描述了寫內存位於第五行的塊分配函數malloc()之後.
按照報告的順序修正錯誤很有必要,因爲後面的錯誤可能是前面的錯誤造成的;
如果不這麼做,會導致Memcheck的使用變得很困難;
內存泄漏的信息通常如下:
==19182== 40 bytes in 1 blocks are definitely lost in
loss record 1 of 1
==19182== at
0x1B8FF5CD: malloc (vg_replace_malloc.c:130)
==19182== by 0x8048385: f (a.c:5)
==19182== by 0x80483AB: main
(a.c:11)
函數調用棧說明了泄漏的內存是在哪分配的;
但是,Memcheck並不能說明爲什麼內存泄漏的(忽略"vg_replace_malloc.c",它只是一個實現細節);
有多種類型的內存泄漏,最重要是如下:
. "definitely lost": 你的程序內存泄漏了 --
需要解決它;
. "probably
lost": 你的程序內存泄漏了,可能需要解決;
除非你對指針做了一些特殊的處理(如將其指向堆的中部)
Memcheck同樣會報告未初始化值的使用,
對於這種情況,通常的消息是"Conditional
jump or move depends on uninitialised value(s)";
追蹤這種錯誤的根源可能很難;
可以嘗試使用 “--track-origins=yes”選項來輸出額外的信息;
但這會使Memcheck運行得更慢,但有可能追查到未初始化值的根源;