valgrind是一个用于检测debug内存泄漏的命令行工具

安装遵循如下命令

下载

1
wget https://sourceware.org/pub/valgrind/valgrind-3.21.0.tar.bz2

解压

1
2
tar -xf valgrind-3.21.0.tar.bz2
cd valgrind-3.21.0

如果ubuntu在解压的时候出现如下报错

1
2
3
4
5
❯ tar -xf valgrind-3.21.0.tar.bz2 
tar (child): lbzip2: Cannot exec: No such file or directory
tar (child): Error is not recoverable: exiting now
tar: Child returned status 2
tar: Error is not recoverable: exiting now

需要安装 lbzip2 包

1
sudo apt install -y lbzip2  

安装完毕就可以正常解压了。

配置/安装,需要有root权限

在解压后的目录里面执行如下命令。

1
2
3
sudo ./configure
sudo make
sudo make install

使用mencheck

mencheck是valgrind的一个模块,其还拥有其他功能,但是我们在这里不做使用。

1
2
3
 --tool=memcheck          选择memcheck工具
 --log-file=log.txt  指定日志输出文件到 log.txt 
 --leak-check=no|summary|full  指定输出日志的详细程度

基本使用命令的一个示例,最后的 ./test 是一个可执行文件

1
valgrind --tool=memcheck --log-file=log.txt --leak-check=full ./test

说明

memcheck模块检测的范围包括

  • 使用未初始化的内存
  • 使用已经被释放的内存
  • 使用超出malloc分配空间的内存
  • 对堆栈的非法访问
  • 是否有释放申请的空间
  • memcpy中src和dst的重叠(参数的内存空间有重合部分)

注意,对于某些new和malloc出来的常驻对象(比如全局的某个对象,单例模式中就会遇到)并不能算作内存泄漏,因为它是需要被使用的。

测试

整了个最简单的代码  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

void test_leak()
{
int *p = (int *)malloc(sizeof(int));
*p = 1;
return;
}

int main()
{
int *p1 = (int *)malloc(sizeof(int));
*p1 = 2;

int *p2 = (int *)malloc(sizeof(int));
*p2 = 3;
// 只析构一个,看看能不能检查出报错
free(p2);
test_leak();// 只有调用的模块出现内存泄漏才能被检测到
// 如果不调用这个函数,则不会检测到这里的问题
// 因为valgrind是操作监看可执行文件的,并不是来扫描代码的

return 0;
}

这里面有3个malloc,其中有两个是没有free的,那么用valgrind可以检测出来吗?

答案是肯定的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
==24089== Memcheck, a memory error detector
==24089== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==24089== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==24089== Command: ./test
==24089== Parent PID: 24056
==24089==
==24089==
==24089== HEAP SUMMARY:
==24089== in use at exit: 8 bytes in 2 blocks
==24089== total heap usage: 4 allocs, 2 frees, 72,716 bytes allocated
==24089==
==24089== 4 bytes in 1 blocks are definitely lost in loss record 1 of 2
==24089== at 0x484880F: malloc (vg_replace_malloc.c:431)
==24089== by 0x1091F8: main (in /home/lyk/ub-vm/test)
==24089==
==24089== 4 bytes in 1 blocks are definitely lost in loss record 2 of 2
==24089== at 0x484880F: malloc (vg_replace_malloc.c:431)
==24089== by 0x1091C1: test_leak() (in /home/lyk/ub-vm/test)
==24089== by 0x10922C: main (in /home/lyk/ub-vm/test)
==24089==
==24089== LEAK SUMMARY:
==24089== definitely lost: 8 bytes in 2 blocks
==24089== indirectly lost: 0 bytes in 0 blocks
==24089== possibly lost: 0 bytes in 0 blocks
==24089== still reachable: 0 bytes in 0 blocks
==24089== suppressed: 0 bytes in 0 blocks
==24089==
==24089== For lists of detected and suppressed errors, rerun with: -s
==24089== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)

在最后的summry里面可以看到,这里提示 definitely lost 了8个字节,而且是在两个不同的区块中丢失的。和我们的代码吻合。

如果你不调用  test_leak()  函数,那么就不会出现第二次的内存泄漏,报告会变成只有  4 bytes in 1 blocks ,因为valgrind是通过可执行文件来检测是否有内存泄漏的,他不会扫描你的代码(那样的成本太高了而且不一定准确!)