内存泄漏检测

来源:互联网 发布:浙江出版联合集团知乎 编辑:程序博客网 时间:2024/05/29 04:49

内存泄漏检测

sf2gis@163.com

2013年5月14日

 

1  内存泄漏

1.1 定义

Memory leaks,也称存储渗漏。动态分配的内存空间,在使用完成后没有释放,导致程序一直占据该内存单元,直到程序结束。

当程序申请了一块内存,但指向这块内存的指针销毁了。那么这块内存无法释放,则此块内存泄漏了。

1.2 表现

内存泄漏导致程序占据的内存越来越多,直到程序结束或内存资源全部占用,系统崩溃。

1.3 分类

1.3.1 常发性

多次执行,每次都会有泄漏发生。

1.3.2 偶发性

特定环境下发生泄漏。

1.3.3 一次性

每次仅发生一块泄漏

1.3.4 隐式

程序不断申请内存,但却在程序结束时才释放,长时间运行时会产生与内存泄漏一样的效果。

1.4 避免

1.4.1 尽量减少手动分配内存,使用其它方式代替。

1.4.2 数组应使用stl容器代替。

1.4.3 尽量使用智能指针。

1.4.4 New和Delete的成对使用。这里要注意异常时要释放资源。

1.4.5 所有继承使用虚析构函数,所有接口使用定义这空的虚析构函数,避免无法级联析构造成内存泄漏。

2 内存泄漏检测

MS的官方支持MFC内存泄漏方法,其它C++文件也可以检测,但无法定位。

此方法可以将malloc和free用_malloc_debug和_free_debug替换,可以跟踪内存分配。如果检测到预定义的_CRT_MAP_ALLOC,则可以输出内存泄漏所在的代码位置。

2.1 概要步骤

1)   添加支持文件

2)   设置结束后内存泄漏检测

3)   多运行几次,确定固定的内存分配号

4)   根据输出的内存分配号,设置内存断点

5)   在运行到内存断点后,查看调用堆栈,找到泄漏点

2.1.1 示例

//check memory leak

#define _CRT_MAP_ALLOC

#include <cstdlib>

#include <crtdbg.h>

。。。

  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);

。。。

。。。

  _CrtSetBreakAlloc(13810);

。。。

。。。

找到内存泄漏点并修复。

2.2 添加支持文件

#define _CRT_MAP_ALLOC//定位功能

#include <stdlib.h>//crtdbg支持库

#include <crtdbg.h>//内存泄漏检测库

注意顺序固定!

2.3 输出检测信息

_CrtDumpMemeryLeaks():输出检测信息,检测此前的内存泄漏状态。

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF):设置内存泄漏检测状态,这样可以在每次退出时自动测试,适用于多个可能出口的情况。

3 内存检测报告

3.1 报告格式

{内存分配序号} 块类型 block at 内存位置(16进制表示),内存泄漏数量 bytes long,Data:<前16字节内容> 每个字节的内容(16进制表示)。

3.1.1 注释

内存分配序号:当前内存块被依次分配的序号。

3.2 示例

{366}normal block at 0x0020FEA8, 200 bytes long. Data: <                > CD CD CD CD CD CD CD CD CDCD CD CD CD CD CD CD

3.3 调试信息输出到输出窗口

3.3.1 格式

_RPTn(类型,内容,参数);n表示参数的个数。参数格式与printf格式相同。

变体有:_RPTFn(),_RPTWn(),_RPTFWn()。

 

类型有三种:

_CRT_WARN:在输出窗口显示。

_CRT_ERROR:以调试错误窗口形式显示。

_CRT_ASSERT:以调试断言错误窗口形式显示。

内容:格式化输出方式,与后面的参数对应。

参数:参数的数目要在_RPTn()中由n指定。

3.3.2 示例

    _RPT0(_CRT_ERROR,"Type:_CRT_ERROR!");

    _CrtMemDumpStatistics(&s1);

    _RPT0(_CRT_ASSERT,"Type:_CRT_ASSERT");

    _CrtMemDumpStatistics(&s2);

    _CrtMemDifference(&s3,&s1,&s2);

    _RPT1(_CRT_WARN,"%s","Type:_CRT_WARN\n");

 

 

4 内存分配号的使用:内存断点

内存分配号可以在Quick Watch、Watch或者代码中指定在内存分配到此号时,程序进入中断调试状态。

注意:由于内存分配号并不是保持不变的,所以在使用时,要注意此号是否会改变。

内存断点的从分配后的高次序向低次序进行调试,因为最后的内存泄漏可能导致其操作中其它的内存没有释放。

4.1 在代码中设置分配内存断点(静态断点)

在窗口中设置内存断点每次都要重新设置value值,操作步骤也比较麻烦,可以使用代码中设置断点。

_CrtSetBreakAlloc(value);或_CrtBreakAlloc= value;

两都效果相同。

如:_CrtSetBreakAlloc(366);与_CrtBreakAlloc= 366;效果是一样的。

 

4.2 在监视窗口中设置分配内存断点(动态断点)

4.2.1 首先确定自己的程序使用的C运行库类别

方法是程序Propertise -> C/C++ -> Code Generation-> Runtime Library选项,若为Multi-threaded Debug(/MTd),则为静态链接,若为Multi-threaded Debug DLL(/MDd),则为动态链接。

4.2.2 设置监视位置

    然后按F11启动程序,程序会停在入口点。此时调出Watch窗口,动态设置监视的内存地址。

a)   静态链接 在name项中输入_crtBreakAlloc,在value项中输入你要定位的内存分配编号;

b)   动态链接 在name项中输入{,,msvcr100d.dll}_crtBreakAlloc,在value项中输入内存分配号。

注意:msvcr100d.dll是vc2010环境使用的Debug运行库dll,若你用的是其他版本vc,请换成对应版本的dll。

注意:这种方法每次运行都会重置value值为-1,所有要注意每次运行时设置value值。

4.2.3 调试程序

断点中断之后,就可以查看调用堆栈,找到中断位置的上下文,查找可能出现问题的位置。

 

5 内存块类型

内存块类型:自由块,忽略块,普通块,客户端块,CRT块。

自由块:处理内存中,可以被使用的块。

忽略块:被特殊标示的块。

普通块:程序中使用的自由存储块。

客户端块:MFC为需要析构对象创建的内存块。

CRT块:CRT内部使用的块。

6 内存状态比较

比较内存中各种内存块的数量,可以确定在不同时刻内存的使用状态,如果两个时刻的内存状态应该保持一致,则可以此为据确定有没有发生内存泄漏。

6.1 内存状态快照结构

_CRT_MEM_STATE:内存状态结构。

6.2 获取内存状态快照

_CrtMemCheckPoint (_CRT_MEM_STATE * m):将当前内存状态装入内存状态结构中。

6.3 导出内存状态快照

_CrtMemDumpStatistic(const _CRT_MEM_STATE * m):将当前内存状态输出到输出窗口中。

6.4 比较内存状态快照

_CrtMemDifference(_CRT_MEM_STATE * md,const_CRT_MEM_STATE * mold, _CRT_MEM_STATE * mnew):将新旧两个快照比较,将结构输出到差异状态md中。

6.5 示例

    _CrtMemState s1,s2,s3;

    _CrtMemCheckpoint(&s1);

    char *p = new char[200];

    _CrtMemCheckpoint(&s2);

    _CrtMemDifference(&s3,&s1,&s2);

    _CrtMemDumpStatistics(&s3);

 

7 Bounds Checker检测内存泄漏

Micro Focus公司出口的Dev PartnerBoundsChecker可以检测内存泄漏,能够集成到VS之中,运行时自动检测,使用方便。

7.1 下载安装BoundsCheck

版本:DevPartner Studio Visual C++ Edition 10.0(vs2010可用)

7.2 运行检测

点击BoundsCheck的运行检测按钮,进入BoundsCheck的检测运行状态。

 

7.3 查找修改

如果出现内存泄漏,根据提示,找到出现的源代码位置,进行检查,修改代码。

只需要修改代码中编辑的地方,系统自动填写的代码不用处理。

0 0