前段时间,客户现场的一台服务器上跑的应用占用内存不停的增长,最后把系统内存所有耗完,被系统kill掉了,查看日志报out of memory。因而火急火燎的开始分析内存泄露的可能,差很少一个月左右的时间,都在上面耗着,一直找不到内存泄露的地方。bootstrap
虽然尚未找到内存泄露的具体缘由,可是在网上找到了一个好的内存泄露分析工具,特作记录。
服务器
1、 安装
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
2、快速使用指南
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运行得更慢,但有可能追查到未初始化值的根源;ide