内存泄漏检测
什么是内存泄漏?
内存泄漏不是指物理内存的消失, 而是失去了对这块内存的控制, 导致内存浪费
对于 C/C++ 这种没有垃圾回收机制的语言来说, 我们一般关注两种类型的内存泄漏
堆内存泄漏: malloc或new出来的空间没有free/delete
系统资源泄漏: 申请的某些系统资源没有调用相关函数释放
例如: 文件描述符fd, 套接字socket
如何检测内存泄漏 ?
void GetMemory(char **p, int num)
{
*p = (char*)malloc(sizeof(char) * num);
}
int main(int argc, char** argv)
{
char *str = NULL;
GetMemory(&str, 100);
cout << "内存泄漏!" << endl;
if (str == NULL)
cout << "str = NULL" << endl;
system("pause");
return 0;
}
我们以这个程序为例, 分析Windows和Linux下内存泄漏的检测方法
Windows 平台下
Windows 平台下面 Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法
原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。
用法:
1, 在程序中包含以下代码(顺序不能变)
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
通过包括 crtdbg.h,将 malloc 和 free 函数映射到它们的调试版本即 _malloc_dbg 和 _free_dbg,这两个函数将跟踪内存分配和释放
此映射只在调试版本(在其中定义了_DEBUG)中发生。
发布版本使用普通的 malloc 和 free 函数。
#define 语句将 CRT 堆函数的基版本映射到对应的“Debug”版本。
并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少
2, 在添加了上述语句之后,可以通过在程序中包括以下语句(通常应恰好放在程序退出位置之前)来转储内存泄漏信息
_CrtDumpMemoryLeaks();
此时代码如下:
int main(int argc, char** argv)
{
char *str = NULL;
GetMemory(&str, 100);
cout << "内存泄漏!" << endl;
if (str == NULL)
cout << "str = NULL" << endl;
_CrtDumpMemoryLeaks();
system("pause");
return 0;
}
此时对程序进行调试, 会在输出窗口看到以下信息, 检测到了内存泄漏, 并且可以看到是哪一行导致的
如果程序总是在同一位置退出,调用 _CrtDumpMemoryLeaks() 将非常容易
如果程序从多个位置退出,则无需在每个可能退出的位置放置 _CrtDumpMemoryLeaks()而可以在程序开始处包含以下调用:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
该语句在程序退出时自动调用 _CrtDumpMemoryLeaks()
如何定位内存泄漏具体的位置
通过上面的方法,我们几乎可以定位到是哪个地方调用内存分配函数malloc和new等
如上例中的 GetMemory 函数中,即第11行, 但是不能定位到在哪个地方调用GetMemory导致的内存泄漏,而且在大型项目中可能有多处调用GetMemory
如何定位到在哪个地方调用GetMemory导致内存泄漏呢?
定位内存泄漏的另一种技术的关键点在于: 对应用程序的内存状态拍快照
CRT 库提供一种结构类型 _CrtMemState, 可用它存储内存状态的快照
_CrtMemState s1, s2, s3; // 定义内存状态对象
若要在给定点对内存状态拍快照,请向 _CrtMemCheckpoint
函数传递 _CrtMemState 结构
该函数用当前内存状态的快照填充此结构:_CrtMemCheckpoint( &s1 );
通过向 _CrtMemDumpStatistics
函数传递 _CrtMemState 结构,可以在任意点转储该结构的内容:_CrtMemDumpStatistics( &s1 );
若要确定代码中某一部分是否发生了内存泄漏,可以在该部分之前和之后对内存状态拍快照,然后使用 _CrtMemDifference
比较这两个状态
_CrtMemCheckpoint( &s1 );
// 可能产生内存泄漏的地方
_CrtMemCheckpoint( &s2 );
if( _CrtMemDifference( &s3, &s1, &s2) )
_CrtMemDumpStatistics( &s3 );
例如上例代码可改为
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include<iostream>
using namespace std;
_CrtMemState s1, s2, s3;
void GetMemory(char **p, int num)
{
*p = (char*)malloc(sizeof(char) * num);
}
int main(int argc, char** argv)
{
_CrtMemCheckpoint(&s1);
char *str = NULL;
GetMemory(&str, 100);
_CrtMemCheckpoint(&s2);
if (_CrtMemDifference(&s3, &s1, &s2))
_CrtMemDumpStatistics(&s3);
cout << "内存泄漏!" << endl;
if (str == NULL)
cout << "str = NULL" << endl;
_CrtDumpMemoryLeaks();
system("pause");
return 0;
}
说明 s1 和 s2 之间有内存泄漏 !
Linux 平台下
mtrace 工具
引头文件 mcheck.h
在程序开始处调用函数 mtrace()
上例程序可以改写如下
#include <mcheck.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
void getMemory(char** p, int size)
{
*p = (char*)malloc(sizeof(char)*size);
}
int main()
{
mtrace();
char* str = NULL;
getMemory(&str, 100);
if(str == NULL)
{
cout << "内存申请失败" << endl;
}
else
{
cout << "内存申请成功" << endl;
}
return 0;
}
使用方法: 先导入环境变量 export MALLOC_TRACE=log.txt
编译程序 g++ test.cpp -o main -g
生成 main 可执行程序
./main
运行程序, 生成 log.txt 文件
用命令 mtrace 查看 log.txt 文件
mtrace main log.txt
打印出了错误信息, 内存没有释放, 是在 test.cpp 的第10行出了问题
ps: 可能你的Linux上没有安装 mtrace 工具
可以试试 yum install glibc-utils
来安装, 我的Linux上刚开始就没有这个工具, 然后网上搜了一下, 安装了这个 glibc-utils
就好了
valgrind 工具
在 Linux 下, 还有一个更常用的工具: valgrind
大家可以去官网 http://www.valgrind.org/ 下载安装包
解压 tar –jxvf valgrind-3.13.0.tar.bz2
进入目录 cd valgrind-3.13.0/
依次执行
./autogen.sh
./configure
make
sudo make install
安装过程可能因环境不同遇到各种问题, 大家可以上网搜索一下相关解决方法
下面来看看如何使用它
首先还是这个示例程序
#include <iostream>
#include <stdlib.h>
using namespace std;
void getMemory(char** p, int size)
{
*p = (char*)malloc(sizeof(char)*size);
}
int main()
{
char* str = NULL;
getMemory(&str, 100);
if(str == NULL)
{
cout << "内存申请失败" << endl;
}
else
{
cout << "内存申请成功" << endl;
}
return 0;
}
我们不用在代码中加任何其他东西
然后编译程序 执行命令
valgrind --tool=memcheck --leak-check=full ./main
我们可以看到输出了一大堆信息, 我们清楚地看到是哪一行, 哪个函数的调用导致了内存泄漏, 真的很方便呢
valgrind 还有很多其他的选项, 感兴趣的小伙伴可以再了解了解