使用 Valgrind 检测 C++ 内存泄漏

本文介绍了Valgrind,一种强大的内存错误检测工具,用于检测C/C++程序中的内存泄漏、越界访问和未初始化内存。通过实例演示如何安装、配置并使用Valgrind进行内存检查,以及如何解读其输出结果来修复常见错误。

Valgrind 的介绍
  Valgrind 可以用来检测程序是否有非法使用内存的问题,例如访问未初始化的内存、访问数组时越界、忘记释放动态内存等问题。在 Linux 可以使用下面的命令安装 Valgrind:

1
2
3
4
5
6
$ wget ftp://sourceware.org/pub/valgrind/valgrind-3.13.0.tar.bz2
$ bzip2 -d valgrind-3.13.0.tar.bz2
$ tar -xf valgrind-3.13.0.tar
$ cd valgrind-3.13.0
$ ./configure && make
$ sudo make install
检测内存泄漏
  Valgrind 可以用来检测程序在哪个位置发生内存泄漏,例如下面的程序:

1
2
3
4
5
6
7
8
#include <stdlib.h>
int main()
{
int *array = malloc(sizeof(int));
return 0;
}
  编译程序时,需要加上-g选项:

1
$ gcc -g -o main_c main.c
  使用 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
$ valgrind --tool=memcheck --leak-check=full ./main_c
31416 Memcheck, a memory error detector
31416 Copyright © 2002-2017, and GNU GPL’d, by Julian Seward et al.
31416 Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
31416 Command: ./main_c
31416
31416
31416 HEAP SUMMARY:
31416 in use at exit: 4 bytes in 1 blocks
31416 total heap usage: 1 allocs, 0 frees, 4 bytes allocated
31416
31416 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
31416 at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
31416 by 0x400537: main (main.c:5)
31416
31416 LEAK SUMMARY:
31416 definitely lost: 4 bytes in 1 blocks
31416 indirectly lost: 0 bytes in 0 blocks
31416 possibly lost: 0 bytes in 0 blocks
31416 still reachable: 0 bytes in 0 blocks
31416 suppressed: 0 bytes in 0 blocks
31416
31416 For counts of detected and suppressed errors, rerun with: -v
31416 ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  先看看输出信息中的HEAP SUMMARY,它表示程序在堆上分配内存的情况,其中的1 allocs表示程序分配了 1 次内存,0 frees表示程序释放了 0 次内存,4 bytes allocated表示分配了 4 个字节的内存。
  另外,Valgrind 也会报告程序是在哪个位置发生内存泄漏。例如,从下面的信息可以看到,程序发生了一次内存泄漏,位置是main.c文件的第 5 行:

1
2
3
31416 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
31416 at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
31416 by 0x400537: main (main.c:5)
  Valgrind 也可以用来检测 C++ 程序的内存泄漏,下面是一个正常的 C++ 程序,没有发生内存泄漏:

1
2
3
4
5
6
7
8
9
#include
int main()
{
auto ptr = new std::string(“Hello, World!”);
delete ptr;
return 0;
}
  使用 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
$ valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./main_cpp
31438 Memcheck, a memory error detector
31438 Copyright © 2002-2017, and GNU GPL’d, by Julian Seward et al.
31438 Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
31438 Command: ./main_cpp
31438
31438
31438 HEAP SUMMARY:
31438 in use at exit: 72,704 bytes in 1 blocks
31438 total heap usage: 2 allocs, 1 frees, 72,736 bytes allocated
31438
31438 72,704 bytes in 1 blocks are still reachable in loss record 1 of 1
31438 at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
31438 by 0x4EC3EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
31438 by 0x40104E9: call_init.part.0 (dl-init.c:72)
31438 by 0x40105FA: call_init (dl-init.c:30)
31438 by 0x40105FA: _dl_init (dl-init.c:120)
31438 by 0x4000CF9: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
31438
31438 LEAK SUMMARY:
31438 definitely lost: 0 bytes in 0 blocks
31438 indirectly lost: 0 bytes in 0 blocks
31438 possibly lost: 0 bytes in 0 blocks
31438 still reachable: 72,704 bytes in 1 blocks
31438 suppressed: 0 bytes in 0 blocks
31438
31438 For counts of detected and suppressed errors, rerun with: -v
31438 ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
  使用 Valgrind 分析 C++ 程序时,有一些问题需要留意。例如,这个程序并没有发生内存泄漏,但是从HEAP SUMMARY可以看到,程序分配了 2 次内存,但却只释放了 1 次内存,为什么会这样呢?
  实际上这是由于 C++ 在分配内存时,为了提高效率,使用了它自己的内存池。当程序终止时,内存池的内存才会被操作系统回收,所以 Valgrind 会将这部分内存报告为 reachable 的,需要注意,reachable 的内存不代表内存泄漏,例如,从上面的输出中可以看到,有 72704 个字节是 reachable 的,但没有报告内存泄漏。

检测越界访问
  C++ 程序经常出现的 Bug 就是数组越界访问,例如下面的程序出现了越界访问:

1
2
3
4
5
6
7
8
9
10
#include
#include
int main()
{
std::vector v(10, 0);
std::cout << v[10] << std::endl;
return 0;
}
  使用 Valgrind 分析这段程序,Valgrind 会提示越界访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ g++ -std=c++11 -g -o main_cpp main.cpp
$ valgrind --tool=memcheck --leak-check=full ./main_cpp
31523 Memcheck, a memory error detector
31523 Copyright © 2002-2017, and GNU GPL’d, by Julian Seward et al.
31523 Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
31523 Command: ./main_cpp
31523
31523 Invalid read of size 4
31523 at 0x400AD7: main (main.cpp:7)
31523 Address 0x5ab5ca8 is 0 bytes after a block of size 40 alloc’d
31523 at 0x4C2E216: operator new(unsigned long) (vg_replace_malloc.c:334)
31523 by 0x4010D3: __gnu_cxx::new_allocator::allocate(unsigned long, void const*) (new_allocator.h:104)
31523 by 0x401040: std::allocator_traits<std::allocator >::allocate(std::allocator&, unsigned long) (alloc_traits.h:491)
31523 by 0x400F91: std::_Vector_base<int, std::allocator >::_M_allocate(unsigned long) (stl_vector.h:170)
31523 by 0x400E7E: std::_Vector_base<int, std::allocator >::_M_create_storage(unsigned long) (stl_vector.h:185)
31523 by 0x400D1E: std::_Vector_base<int, std::allocator >::_Vector_base(unsigned long, std::allocator const&) (stl_vector.h:136)
31523 by 0x400C11: std::vector<int, std::allocator >::vector(unsigned long, int const&, std::allocator const&) (stl_vector.h:291)
31523 by 0x400AB9: main (main.cpp:6)
  Invalid read of size 4表示越界读取 4 个字节,这个操作出现在main.cpp文件的第 7 行。另外可以看到,vector分配了一块 40 字节的内存,程序越界访问紧急着这块内存之后的 4 个字节。

检测未初始化的内存
  另一种经常出现的 Bug,就是程序访问了未初始化的内存。例如:

1
2
3
4
5
6
7
8
9
10
11
12
#include
int main()
{
int x;
if (x == 0)
{
std::cout << “X is zero” << std::endl;
}
return 0;
}
  使用 Valgrind 检测这个程序:

1
2
3
4
5
6
7
8
9
$ g++ -std=c++11 -g -o main_cpp main.cpp
$ valgrind --tool=memcheck --leak-check=full ./main_cpp
31554 Memcheck, a memory error detector
31554 Copyright © 2002-2017, and GNU GPL’d, by Julian Seward et al.
31554 Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
31554 Command: ./main_cpp
31554
31554 Conditional jump or move depends on uninitialised value(s)
31554 at 0x400852: main (main.cpp:6)
  输出中提示了main.cpp文件的第 6 行访问了未初始化的内存。

参考资料
Chapter 17. The memory profiler Valgrind
Using Valgrind to Find Memory Leaks and Invalid Memory Use

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值