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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值