Linux内存泄漏检测工具Valgrind,轻松定位C/C++内存错误

检测维修 0 62

Valgrind是用于x86架构Linux的, 具备多重用途的代码剖析以及内存调试功用的工具。你能够于它所营造的环境里, 运行你的程序, 以此监视内存的使用情形, 例如在C语言当中的malloc和free, 又或者是在C++里面的new和delete。要是你使用了尚未初始化的内存, 在数组末端之外设置内存情形, 或者是忘却释放指针, 皆是Valgrind能够检测出来的状况。即便Valgrind能够开展别的工作, 这套教程依旧聚焦于怎样运用它去找出与内存有关的错误之处, 这是由于这也是程序员常常会犯的错误。

.Windows系统的用户无需感到沮丧, 尽管于该Windwos操作系统之上不存在可得用的Valgrind, 可是于其之上你能够去试着使用一下IBM推出的Purify, 它在具备有功能方面跟Valgrind是相类似的。

要是你正在使用Linux, 然而却没装有Valgrind, 那可以前往此处, 免费下载一份获得Valgrind。

安装的时候特别容易, 只要用以bzip2来进行解压缩, 把下载的软件包给展开就行, (在下面所举例子里的XYZ是版本号)。

bzip2 -d valgrind-XYZ.tar.bz2

tar -xf valgrind-XYZ.tar

或者用更简单的方法:

tar jxf valgrind-XYZ.tar.bz2

此时会生成一个名为valgrind - XYZ的目录, 步入该目录然后进行运行。

./configure

make

make install

如今, 你已然把Valgrind给安装好了, 接下来, 便能够着手去知晓怎样运用它。

利用 Valgrind 去查找内存泄漏这一情况, 内存泄漏属于是非常不容易被发觉到的极为常见的错误之中的一种, 原因在于, 要直至消耗完内存或者调用 malloc 是败北的方才会最终致使出现任何故障。实质而言, 当运用诸如 C 或者 C++ 这类不存在垃圾回收这般机制的语言进行操作时, 你占据一大半时间的情况都是支出被用到如何对头头是道地去释放内存。要是程序整体运行的光阴足够漫长的话, 哪怕微小的一出差错也将会对于程序产生意义不凡的影响呀。

Valgrind有着诸多支持工具, 诸如Memcheck, Addrcheck, Cachegrind, Massif, Helgrind以及Callgrind等等。当运行Valgrind之时, 当中你必须予以指明打算运用的工具。于这篇教程里边, 我们大体聚焦于内存检查工具之上, 这工具能够协助我们去检查内存使用状况, 呵呵, 其他工具我着实不会运用。若不存在别的什么参数的话, Valgrind 于程序终止完毕之后给出就 free 以及 malloc 总共调用次数来讲的简报;(请注意一下;此次所给到的值里面 18490 是进程号;在你所操作的机台上很可能是其余别的某个值)

百分号, 利用valgrind工具, 且该工具为内存检查工具, 运行名为program_name的程序。

...

等于18515, 等于, 动态内存分配/释放: 在程序退出时正在使用: 0字节, 存在于0个内存块中。

18515这个标识下, 存在着动态内存分配与释放的情况, 具体为有一次分配操作, 有一次释放操作, 总共分配了十个字节的内存空间。

想要得到一份详尽的泄漏分析结果的话, 再次运行时需采用: --leak-check=yes , 以此来进行操作。

要是程序里存在内存泄漏这种状况, 那么内存分配及释放的数量会出现不一致的情况, 这里需要注意的是你绝不能够借助一个free调用去释放多个已分配内存。

若程序内存分配与释放数量不对等, 你能够添加leak - check参数再度运行程序, 如此便能瞧见分配了内存却未释放的代码。

我写了一个简单的C程序, 生成"example1"应用, 目的为了演示这个功能, 之后进行编译。

#include

int main()

字符指针x被赋予通过动态存储分配函数(在C语言中是malloc, 其参数为100;或者在C++中是new char)所分配的内存空间的地址。

return 0;

百分之, 使用valgrind工具, 其工具为memcheck, 进行内存检查, 检查方式是泄漏检查, 且泄漏检查为开启状态, 针对example1。

在运行的结果里头, 给出了调用那malloc然而却没有调用free的函数的列表。

在1个丢失记录中的第1条丢失记录里, 确切地说, 1个块中的100字节肯定是丢失了, 此记录为1条中的第1条。

在地址为0x1B900DD0处, 发生了名为malloc的行为, 其相关代码位于vg_replace_malloc.c文件的第131行。

这个由其地址为0x804840F的函数调用所导致的结果为2116。这个函数是main函数。它所在的路径是/home/cprogram/example1。

上面得出的结果, 并未告知我们更多所需的信息, 我们仅仅晓得, 在main函数里的malloc调用致使了内存泄漏, 然而并不知道是程序中的哪一行调用了malloc, 这是由于我们在编译程序的时候, 没有给gcc加上-g参数, 相关的调试信息便丢失了, 重新编译一次再运行, 我们就获取到了更多的信息(片断)。

等于两千三百三十, 百个字节, 处于一个块内, 明确地, 在损失记录当中失去, 损失记录为一, 总共是一个记录, 共计一百个字节, 处于一个块内完全失去了, 肯定是这样。

==2330== 在 0x1B900DD0 这个位置, 进行了 malloc 操作, 出自 vg_replace_malloc.c 文件的第 131 行。

意思是通过位于示例1.c文件第5行由0x804840F表示的 主函数, 等于2330。

当下, 我们已然确切晓得致使内存泄漏的究竟是那一行代码了。虽说知晓在何处释放内存依旧是个难题, 不过好歹我们已经清楚该从哪儿着手寻觅了。鉴于针对每一回需要动态分配之内存, 你皆拥有一份关于何时进行分配, 以及何时予以释放的使用规划, 既然已然明晰致使内存泄漏的具体分配地点, 如此一来也就大体梳理清了内存的 使用计划, 这对于精准寻得释放内存的正确位置颇有助益。

在加上那个被称作--leak-check=yes的参数之后, 在不再显示内存泄漏错误之前, 这段过程里你有可能需要反反复复地去修改代码好多好多回, 如此这般一个堪称优秀的、不存在内存泄漏问题的软件方才得以诞生, 就是这样的情况:-)。在运行Valgrind这个工具的时候, 加上那个标记为--show-reachable=yes的参数, 能够找寻到每一个未来会匹配的free或者new, 输出的结果和上面所讲的情形大致相同, 只不过展示出了更多尚未被释放的内存。

使用Valgrind的memcheck工具来查找无效指针使用, Valgrind同样能够找出无效堆内存使用, 比如说,万一用malloc或者是new分配了一个数组, 然后对数组末端背后的内存去进行访问:。

char *x = malloc(10);

x = ´a´;

能检测出这个错误的是Valgrind, 运行下面示例程序即example2要用Valgrind。

#include

int main()

char *x = malloc(10);

x = ´a´;

return 0;

Valgrind内存调试工具教程_linux 内存泄漏检测工具_Valgrind内存泄漏检测

使用valgrind工具, 其工具为memcheck, 去进行检查, 检查设置漏为leak-check且值是yes的情形, 针对example2进行操作。

其结果是(片断)

出现了无效写入, 写入的大小是1, 此情况对应的编号是9814。

在地址0x804841E处, 是主函数, 其位置在文件tst.c的第6行。

等于9814, 地址0x1BA3607A, 在大小为10的已分配块之后0字节 , 有什么问题吗, 那里有什么情况, 存在这种情况是什么缘由。

等于9814, 在0x1B900DD0处: 进行内存分配这个行为通过vg_replace_malloc.c文件中的第131行代码实现。

等于9814, 由0x804840F造成: 主函数(示例2.c文件: 第5行)。

这个信息意味着我们分配了10个字节的内存, 然而却访问了超出范围的内存, 所以, 我们就开展了一个´非法写´操作。要是尝试从那块内存读取数据, 我们就会获取´Invalid read of size X´的警告(X是尝试读取数据的大小, char是一个字节, 而int依据系统的差异或许是2个字节或者4个字节)。通常而言, Valgrind呈现出函数调用栈信息以便我们精准确定错误。

使用未初始化变量的检测这类操作中有一类是Valgrind能够检测的, 那就是在条件判断语句里使用未初始化变量。或许你应当养成在声明变量之际就予以初始化的习惯,然而Valgrind依旧能够帮你找出使用未初始化变量的所在之处。举例来说, 运行依照下面代码生成的示例程序叫example3。

#include

int main()

int x;

if(x == 0)

选用cout并包含相关头文件来替换printf("X is zero")。

iostream for C++ */

return 0;

Valgrind会给出下面的结果(片断)

存在这样一种情况, 条件跳转或者移动, 依赖于未被初始化的值 , 这一现象被标记为 ==17943==。

==17943== 在 0x804840A处 : 主函数 , 在example3.c文件的行号第6行处出现。

即使是Valgrind, 如果一个变量被赋予一个未初始化的变量, 它也能够知晓, 此时这个变量仍然正处于“未初始化”状态。举例来说就是运行下面这样的代码:

#include

int foo(int x)

if(x < 10)

使用printf函数, 输出内容为"x is less than 10", 并换行。

int main()

int y;

foo(y);

Valgrind会给出下列警告:

在执行相关操作时, 出现了条件跳转, 这一条件跳转或者移动操作, 是依赖于未初始化的值的, 这里含有不止一个未初始化的值。

在0x8048366处, 是foo, 位于example4.c的第5行。

等于4827, 由0x8048394导致, 在主函数处, 位于example4.c文件的第14行。

兴许你会觉得毛病之处在foo里头呢, 和那些在调用栈上边的别的各函数没啥关系。然而由于main这个函数传递了一个未曾初始化的值给foo, 所以我们能够依据调用栈所呈现的信息按部就班地找寻, 从而找出那实则并未对变量进行初始化的代码。

Valgrind仅能助力你于可运行至代码里检测那些错误, 务必要确保在测试期间覆盖代码之每一分支。

Valgrind还能找出啥? Valgrind还能察觉别的不正确运用内存的差错: 要是你针对同一块内存实施了两次释放操作, Valgrind就会探测出来, 并且你会获取到非法free的调用栈信息。

内存释放方法使用不正确的错误, Valgrind也能够检测出来。举例来说, 在C++语言当中, 存在着三种基本的内存释放办法: free, delete并且delete。free函数应该只是跟malloc函数相对应, 于某些系统之上, 你有可能不用面对这个问题, 然而这样是不具备可移植性的。delete应当又只能与new(分配数组)相对应。(或许有的编译器允许你不去在意这些规则, 不过无法保证所有的编译器都准许你这么做, 毕竟这并非标准的构成部分)

如果程序中存在这些问题,你会得到下列错误信息:

不匹配的释放操作, 即自由函数调用 , 删除操作, 以及删除操作。

这些错误都应该被立刻修复,即使你的程序偶然能够正常运行。

Valgrind查不出什么样的错误呢, Valgrind不会给予静态数组(分配在栈上的那种)进行边界方面的检查, 要是在程序里声明了一个数组。

int main()

char x;

x = ´a´;

Valgrind不会对你发出警告!为了便于测试, 你能够将数组变更为动态于堆上分配的数组, 如此一来便有可能开展边界检查。这个办法似乎存在些许得不偿失之感。

Valgrind带来的更多告诫所呈现的负面影响究竟是什么, 它占据了更多的内存, 这一内存量可达两倍于你程序正常使用的量。倘若你运用Valgrind去检测使用大量内存的程序那时便会遭遇问题状况, 它有可能会耗用漫长的时间去运行测试。在大多数情形之下, 此种情况并非是问题所在, 即便速度较为缓慢也仅仅是检测期间速度缓慢而已, 要是你借助Valgrind去检测一个正常运行时速度就已然很慢的程序, 如此一来问题可就严重了。

Valgrind没法检测完你于程序里所犯的全部错误, 要是你不查缓冲区溢出这回事, Valgrind也并不会跟你讲代码写进了它不该写的内存区域。

Valgrind是用于x86架构的工具呵, 仅能在Linux上运行呢(FreeBSD和NetBSD有关版本正处于开发进程中), 它可使程序员于其环境里对程序作测试以检查未配对malloc调用错误还有其它使用非法内存(未初始化内存)产生的错误以及非法内存操作情况(像同一块内存释放两次或调用无误的析构函数), Valgrind并不对静态分配数组使用状况进行检查。

相关推荐: