文章目录
1.使用背景
C/C++的程序员完成程序开发后,除了实现功能以外,还会是否存在内存泄漏的问题,特别是在复杂的应用程序中,肉眼review代码很难完全检查可能存在的出内存的泄漏问题,我们需要借助工具进行内存检测。
2.Valgrind介绍
2.1 基本概念
Valgrind是一个Linux下灵活的调试和剖析可执行工具。它由在软件层提供综合的CPU内核,和一系列调试、剖析的工具组成。架构是模块化的,所以可以在不破坏现有的结构的基础上很容易的创建出新的工具来。
2.2 主要工具
Memcheck
- 用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete的调用都会被捕获。
本文主要介绍的就是使用该工具进行程序的内存泄漏问题的检测和定位。
Callgrind
- 收集程序运行时的一些数据,建立函数调用关系图,还可以有选择地进行cache模拟。在运行结束时,它会把分析数据写入一个文件。
Cachegrind
- Cache分析器,它模拟CPU中的一级缓存I1,Dl和二级缓存,能够精确地指出程序中cache的丢失和命中。
Helgrind
- 它主要用来检查多线程程序中出现的竞争问题。
Helgrind寻找内存中被多个线程访问,而又没有加锁的区域,这些区域往往是线程之间失去同步的地方,而且会导致难以发掘的错误。
Massif
- 堆栈分析器,它能测量程序在堆栈中使用了多少内存,告诉我们堆块,堆管理块和栈的大小。
2.3 特点与不足
特点
- 内存检测位置非常准确,可以确定代码出现问题的位置,这样为迅速解决内存问题非常重要。检测报告易读。
不足
- 由于Valgrind是程序下运行下,才能检测内存错误。另外没有运行到的路径也无法检测到错误。
3.Valgrind安装
CentOS7中直接使用命令进行安装
yum install -y valgrind
4.使用Valgrind进行内存检测
4.1 示例代码
编写不释放内存的示例代码
#include <stdio.h>
int main()
{
int *p_num = new int(10);
int num = 100;
p_num = #
printf("num = %d\n", *p_num);
}
进行编译,注意,在编译过程中加入-g
能够定位内存泄漏的代码位置,帮助我们进行问题排查。
g++ -g -o test.so main.c
4.2 执行工具命令
## 控制台查看
valgrind --tool=memcheck --leak-check=full ./test.so
## 生成检测结果到文件
valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full --show-leak-kinds=all ./test.so
4.3 执行结果查看
==73506== Memcheck, a memory error detector
==73506== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==73506== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==73506== Command: ./test.so
==73506==
num = 100
==73506==
==73506== HEAP SUMMARY:
==73506== in use at exit: 4 bytes in 1 blocks
==73506== total heap usage: 1 allocs, 0 frees, 4 bytes allocated
==73506==
==73506== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==73506== at 0x4C2A593: operator new(unsigned long) (vg_replace_malloc.c:344)
==73506== by 0x40067E: main (main.c:5)
==73506==
==73506== LEAK SUMMARY:
==73506== definitely lost: 4 bytes in 1 blocks
==73506== indirectly lost: 0 bytes in 0 blocks
==73506== possibly lost: 0 bytes in 0 blocks
==73506== still reachable: 0 bytes in 0 blocks
==73506== suppressed: 0 bytes in 0 blocks
==73506==
==73506== For lists of detected and suppressed errors, rerun with: -s
==73506== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
4.4 执行结果分析
主要关注的模块
HEAP SUMMARY
:输出堆上内存变化的统计信息
LEAK SUMMARY
:内存泄漏统计信息
实际分析
valgrind提示在程序退出前,还有4bytes空间未释放,分析日志中提示by 0x40067E: main (main.c:5)
中的代码int *p_num = new int;
没有释放,前面也提到了因为在编译中加了-g
的参数,所以才能在运行介绍后,定位到没有释放内存模块申请内存的具体代码位置。
从代码看,最开始申请了指向一个值为10的int数,然后将指针重新指向了其他地址,于是原来开辟的那块内存就无法进行释放操作了。
按提示释放内存
修改后的代码如下:
#include <stdio.h>
int main()
{
int *p_num = new int(100);
printf("num = %d\n", *p_num);
if (p_num != NULL)
{
delete p_num;
p_num = NULL;
}
}
继续测试,结果如下:
$ g++ -g -o test.so main.c
$ valgrind --tool=memcheck --leak-check=full ./test.so
==77914== Memcheck, a memory error detector
==77914== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==77914== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==77914== Command: ./test.so
==77914==
num = 100
==77914==
==77914== HEAP SUMMARY:
==77914== in use at exit: 0 bytes in 0 blocks
==77914== total heap usage: 1 allocs, 1 frees, 4 bytes allocated
==77914==
==77914== All heap blocks were freed -- no leaks are possible
==77914==
==77914== For lists of detected and suppressed errors, rerun with: -s
==77914== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
5. 其他内存错误
错误提示与原因对应关系如下:
uninitialisedvalue
:使用了未初始化的变量Invalid read/write
:内存读写越界/读写已经释放的内存Source anddestination overlap
:内存覆盖