使用Visual Leak Detect or 检测c/ c++ 程序内存泄漏

来源:互联网 发布:java的回收机制 编辑:程序博客网 时间:2024/05/15 23:39

http://blog.sina.cn/dpool/blog/s/blog_a0f3ea9801010duw.html?vt=4


1.引言
通常的内存泄漏指堆内存泄漏[1]。堆内存指从堆中分配、大小任意、用完须显式释放的内存。程序申请使用内存后,须负责释放,否则就造成内存泄漏。内存泄漏问题很严重,它能导致系统崩溃,所以一定要认真对待,尽快解决。据调查,目前检测泄漏主要有以下几种方法:1. 试探法,根据内存变化情况,不断修改程序返回位置,逐步缩小检查范围,直到定位泄漏点。2.重载内存分配操作符[2],使程序在每次分配和释放内存时做详记录,最后可以根据记录判断何处发生泄漏。3.使用MFC 提供的一系列诊断函数
和类来检测泄漏。4. 使用外挂式的检测工具,如Purify,BoundsChecker[4]等。
目前的这些方法都存在一些问题,需要较多编程经验,且要花许多时间分析和查找,效率很低,虽然一些工具能发挥较大的作用,但也存在一些问题:需设置特殊的编译和链接参数,甚至手工修改源代码,给用户带来负担与风险。而采用Visual Leak Detector 工具可帮忙查找泄漏点,最近参与一个在Visual C++ 6.0 平台的数字图像处理系统开发,遇到比较棘手的就是内存泄漏。在尝试解决这些问题的过程中,本文列出了基本的方法以供参考。
2.内存泄漏分析
简单来说,程序运行时动态申请的内存在使用结束后没有被显式的释放,导致这块内存不能被其它的程序使用,这就造成了内存泄漏[3]。具体造成内存泄漏的可能情况还有:
(1) 在函数出口处未释放动态申请的内存地址空间,C/C++ 函数可以有多个出口,函数可以在任何地方退出,所以一旦有某个出口处(如break,return)没有释放应该释放的内存,就会发生内存泄漏[1]。如下例子,
void MyFunction(int nSize)
{
char* p= new char [nSize];
if( !GetStringFrom( p,nSize ) )
{
MessageBox( “Error”);
return;
}
delete p;
}
(2) 给指针赋值时,没有检查指针是否为空,如果指针不为空,那么指针原来指向的内存将丢失。例如:
BYTE* p = new BYTE[ nSize ];
q = p;
当q 指针不为空时,对其重新赋值后,q 以前指向的内存将丢失而造成泄漏。
(3) 删除指针顺序错误
Test::~Test ( )
{
delete (p) ;
if (NULL ! = p && NULL ! = p - > pData)
{
free (p - > pData) ;
}
}
当p 已经被删除了,那么if 条件永远不成立,这条free语句永远不会执行,即p - > pData 占用的内存没有被释放。从内存泄漏的产生原因我们知道,只要查到内存泄漏的地点,即哪个地方的内存被分配使用后而没有被放掉,我们把它释放掉就可以解决了。在应用程序很大且函数之间调用关系比较复杂的情况下,在这个函数中分配的内存只有在另外一个函数使用后才能删除,人工找出泄漏点效率很低,需要借助于工具检查。
3. 使用Vi sual Leak Det ect or 检测内存泄漏
3.1 Visual Leak Detector 简介
Visual Leak Detector 是一款用于Visual C++ 的免费的
内存泄漏检测工具。相比其它的内存泄漏检测工具,它在检
测到内存泄漏的同时,具有如下特点:
(1) 可以得到内存泄漏点的调用堆栈,及其所在文件及
行号;
(2) 可以得到泄漏内存的完整数据;
(3) 可以设置内存泄漏报告的级别。
Visual Leak Detector 工作原理:
— 81 —
学术探讨软件应用
Visual C++ 内置的工具CRT Debug Heap 工作原理很简单。在使用Debug 版的malloc 分配内存时,malloc 会在内存块的头中记录分配该内存的文件名及行号。当程序退出时CRT 会在main()函数返回之后做一些清理工作,这个时候来检查调试堆内存,如果仍然有内存没有被释放,则一定是存在内存泄漏。从这些没有被释放的内存块的头中,就可以获得文件名及行号。这种静态的方法可以检测出内存泄漏及其泄漏点的文件名和行号,但是并不知道泄漏究竟是如何发生的,也不知道该内存分配语句是如何被执行到的。要想了解这些,就必须要对程序的内存分配过程进行动态跟踪。Visual Leak Detector 就是这样做的。它在每次内存分配时将其上下文记录下来,当程序退出时,对于检测到的内存泄漏,查找其记录下来的上下文信息,并将其转换成报告输出。Visual Leak Detector 要记录每一次的内存分配,而它是如何监视内存分配的呢?Windows 提供了分配钩子(allocation
hooks)来监视调试堆内存的分配。它是一个用户定义的回调函数,在每次从调试堆分配内存之前被调用。在初始化时,Visual Leak Detector 使用_CrtSetAllocHook 注册这个钩子函数,这样就可以监视此后所有的堆内存分配了。如何保证在Visual Leak Detector 初始化之前没有堆内存分配呢?全局变量是在程序启动时就初始化的,如果将Visual Leak Detector作为一个全局变量,就可以随程序一起启动。但是C/C++ 并没有约定全局变量之间的初始化顺序,如果其它全局变量的构造函数中有堆内存分配,则可能无法检测到。
Visual Leak Detector 使用了C/C++ 提供的#pragma init_seg来在某种程度上减少其它全局变量在其之前初始化的概率。根据#pragma init_seg 的定义,全局变量的初始化分三个阶段:首先是compiler 段,一般c 语言的运行时库在这个时候初始化;然后是lib 段,一般用于第三方的类库的初始化等;最后是user 段,大部分的初始化都在这个阶段进行。VisualLeak Detector 将其初始化设置在compiler 段,从而使得它在绝大多数全局变量和几乎所有的用户定义的全局变量之前初始化。
3.2 Visual Leak Detector 的使用方法
首先安装软件Visual Leak Detector 1.9d,然后打开VC
软件菜单Tools –> Options ->Directories,选择Library files,
在下方填入软件安装路径下的lib 文件夹路径,然后选择include files,在下方填入软件安装路径下的include 文件夹路径。接着把vld.h 头文件包含进要测试的内存泄漏程序的源文件中,但要注意一点的是vld.h 文件的包含语句不能放在程序的预编译头文件前面,例如stdafx.h,一般来说只要将#include“vld.h”放在#include“stdafx.h”之后。通过以上设置,就可以调试运行你要测试的程序了。
下面通过一段程序来演示如何使用Visual Leak Detector检测内存泄漏。该程序用new 函数分配了一个int 大小的堆内存,并没有释放。其申请的内存地址用printf 输出到屏幕上。
#include <vld.h>
#include <stdlib.h>
#include <stdio.h>
void f()
{
int *p = new int(0x12345678);
printf('p=x,',p);
}
void main()
{
f();
}
编译运行后,在标准输出窗口得到:
p=003a89c0
在Visual C++ 的Output 窗口得到:
WARNING:Visual Leak Detector detected memory leaks!
---------- Block 57 at 0x003A89C0:4 bytes ---------- --57 号
块0x003A89C0 地址泄漏了4 个字节
Call Stack: -- 下面是调用堆栈
d:\test\testvldconsole\testvldconsole\main.cpp (7): f-- 表示
在main.cpp 第7 行的f()函数
d:\test\testvldconsole\testvldconsole\main.cpp (14):main --
双击以引导至对应代码处
f:\rtm\vctools\crt_bld\self_x86\crt\src\crtexe.c(586):__tmain-
CRTStartup
f:\rtm\vctools\crt_bld\self_x86\crt\src\crtexe.c (403):main-
CRTStartup
0x7C816D4F (File and line number not available):Register-
WaitForInputIdle
Data: -- 这是泄漏内存的内容,0x12345678
78 56 34 12
Visual Leak Detector detected 1 memory leak.
第三行表示57 号块有4 字节的内存泄漏,地址为0x003A89C0,根据程序控制台的输出,可以知道,该地址为指针p。程序的第7 行,f()函数里,在该地址处分配了4 字节的堆内存空间,并赋值为0x12345678,这样在报告中,我们看到了这4 字节同样的内容。可以看出,对于每一个内存泄漏,这个报告列出了它的泄漏点、长度、分配该内存时的调用堆栈和泄漏内存的内容(分别以16 进制和文本格式列出)。双击该堆栈报告的某一行,会自动在代码编辑器中跳到其所指文件的对应行。这些信息对于我们查找内存泄漏将有很大的帮助。通过这个工具,笔者方便地查出了数字图象处理系统中的内存泄漏地点,很快就解决了问题。对上面简单的内存泄漏的例子,当我们发现内存存在泄
漏时,可以人工检查程序,很容易就可以找出问题所在。但对一个大型的软件系统来说,由于程序内部存在多重嵌套调用,并且可能你嵌套调用的程序不是你自己写的,通过人工软件应用学术探讨
Memory Leak Detection in c/c++ Programs with Visual Leak Detector
Yang Lei Wang Renhuang Liu Hongjiang Huang Yingyi
(Guangdong University of Technology,GuangZhou 510090,Guangdong)
【Abstract 】In cognizance of the problem of memory leak detecting,this paper demonstrates how to use Visual Leak Detector to
detect memory Leak via actual examples. Experimentation shows that this method can increase the efficiency when detecting
memory leaks c/c++ programs.
【Keywords 】memory leak;Visual Leak Detector;c/c++

0 0
原创粉丝点击