高效学习与工作之内存泄漏问题的排查与定位
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
-----百科
linux 系统内存泄漏问题的排查与定位
- 平台:linux
- 编程语言:c/c++
- 示例代码:
//TO AS EXAMPLE TO SHOW MEMORY LEAK
//AUTHOR ZTENG @ 20200319
#include "stdio.h"
#include "stdlib.h"
void malloc_mem()
{
char * data = malloc(1024*1024);
}
int main()
{
int i = 10000;
for(int i = 0; i< 10000; i++)
{
malloc_mem();
}
return 0;
}
示例代码1
内存泄漏问题排查
根据“序”中内存泄漏的定义可以看出,内存泄漏问题导致程序运行:
- 程序运行变慢
- 程序运行崩溃
当出现上述现象时,可以怀疑是出现了内存泄漏问题,但要真正的证明则需要查看程序运行的过程中,程序的内存消耗是否呈现递增的现象。
具体做法是,运行程序然后,执行ps -aux指令。
图1
如图所示,左侧窗口编译、运行main.c;右侧端口ps“监视”main运行过程中的内存占用变化,呈不断上升趋势;断定我i程序出现内存泄漏问题。
内存泄漏问题的定位
对于如示例代码所示的20行以内的代码,在明确为内存泄漏问题之后,可以通过走读代码,定位出错误原因。但对于实际开发中的多个文件、上万行代码,走读显然是不太现实的,这个时候可以利用一个叫做Valgrind的内存泄漏检查工具来帮助定位。
图2 如果程序不(意外)结束,有用信息无法输出
图3
对于结果,主要看黄色荧光笔显示的地方:
- Heat Summary部分显示21 allocs,0 frees;
- definitely lost部分显示具体的内存泄露的函数位于alloc_mem函数。
注意:有用信息只会在程序运行完之后汇总输出,如果等不及的话可以对程序ctrl + c来停止信息输出获得有用信息。
内存泄漏问题的解决
不够完备的写法:
void malloc_mem()
{
char * data = malloc(1024*1024);
free(data);
}
示例代码2
如示例代码块2,只做free,这种写法是有风险的,在比较大型的程序中,我们会在程序的末尾对所有的指针进行释放,这个时候为了防止在之前程序中已经释放过,出现过double free的问题,一般会像示例代码3这么做(注意示例代码3和2不是同一块位置,3是指在大型程序结束时,一般会集中回收内存,2是指可能有的时候由于习惯原或者实际需要,因在之前也难免会free内存)。
if (data != NULL)
{
free(data);
}
示例代码3
但这个时候,依旧是会出现double free的问题的。原因在于上面的示例代码3中,data我们只是想当然的认为它如果之前free过,就是NULL,这次就不做free。但实际上打印data的值,如图4所示,是一个非0的数字。
图4
比较正确的内存释放方法是:
void malloc_mem()
{
char * data = malloc(1024*1024);
free(data);
data = NULL;
}
示例代码4
此时,显然打印data的为0,这表明data才是真正的释放,后面才可以用data!=NULL做自己想要的判断:
图5
window内存泄漏检测工具vld
vld 安装:
官网下载可执行文件,直接双击运行即可。
vld使用:
- 头文件中包含#include “vld.h”。
- 将软件安装目录中的vld.ini复制到程序的可执行文件目录下(这样可以保存vld的log信息到txt)。
- 修改vld.ini的内容,主要涉及到3部分:
* ReportEncoding = unicode
* ReportFile = vld_log_1.txt
* ReportTo = both - 运行程序,即可得到如下图所示的log信息。
图6
总结
以一个简单的例子,讲述了linux平台和window平台,内存泄漏问题的现象、排查与定位。使用的工具主要包括ps 指令、valgrind 、vld等。基于本文的方法可以推广至大规模程序代码中快速定位内存泄漏问题。