最快速度找到内存泄漏[转载]

转载链接:http://blog.csdn.net/eric_jo/article/details/4264442

内存管理是C++程序员的痛。我的《内存管理变革系列》就是试图讨论更为有效的内存管理方式,以杜绝(或减少)内存泄漏,减轻C++程序员的负担。

这篇短文我想换个方式,讨论一下如何以最快的速度找到内存泄漏。

确认是否存在内存泄漏

我们知道,MFC程序如果检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏。例如:

class CMyApp : public CWinApp

{

public:

BOOL InitApplication()

{

int* leak = new
int[10];

return TRUE;

}

};

产生的内存泄漏报告大体如下:

Detected memory leaks!

Dumping objects ->

c:/work/test.cpp(186) : {52} normal block at 0×003C4410, 40
bytes long.

Data:
<               
> CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

这挺好。问题是,如果我们不喜欢MFC,那么难道就没有办法?或者自己做?

呵呵,这不需要。其实,MFC也没有自己做。内存泄漏检测的工作是VC++的C运行库做的。也就是说,只要你是VC++程序员,都可以很方便地检测内存泄漏。我们还是给个样例:

#include <crtdbg.h>

inline void EnableMemLeakCheck()

{

_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
| _CRTDBG_LEAK_CHECK_DF);

}

void main()

{

EnableMemLeakCheck();

int* leak = new int[10];

}

运行(提醒:不要按Ctrl+F5,按F5),你将发现,产生的内存泄漏报告与MFC类似,但有细节不同,如下:

Detected memory leaks!

Dumping objects ->

{52} normal block at 0×003C4410, 40 bytes long.

Data:
<               
> CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

为什么呢?看下面。

定位内存泄漏由于哪一句话引起的

你已经发现程序存在内存泄漏。现在的问题是,我们要找泄漏的根源。

一般我们首先确定内存泄漏是由于哪一句引起。在MFC中,这一点很容易。你双击内存泄漏报告的文字,或者在Debug窗口中按F4,IDE就帮你定位到申请该内存块的地方。对于上例,也就是这一句:

int* leak = new int[10];

这多多少少对你分析内存泄漏有点帮助。特别地,如果这个new仅对应一条delete(或者你把delete漏写),这将很快可以确认问题的症结。

我们前面已经看到,不使用MFC的时候,生成的内存泄漏报告与MFC不同,而且你立刻发现按F4不灵。那么难道MFC做了什么手脚?

其实不是,我们来模拟下MFC做的事情。看下例:

inline void EnableMemLeakCheck()

{

_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
| _CRTDBG_LEAK_CHECK_DF);

}

#ifdef _DEBUG

#define new   new(_NORMAL_BLOCK, __FILE__,
__LINE__)

#endif

void main()

{

EnableMemLeakCheck();

int* leak = new int[10];

}

再运行这个样例,你惊喜地发现,现在内存泄漏报告和MFC没有任何分别了。

快速找到内存泄漏

单确定了内存泄漏发生在哪一行,有时候并不足够。特别是同一个new对应有多处释放的情形。在实际的工程中,以下两种情况很典型:

  • ·
    创建对象的地方是一个类工厂(ClassFactory)模式。很多甚至全部类实例由同一个new创建。对于此,定位到了new出对象的所在行基本没有多大帮助。

  • · COM对象。我们知道COM对象采用Reference
    Count维护生命周期。也就是说,对象new的地方只有一个,但是Release的地方很多,你要一个个排除。

那么,有什么好办法,可以迅速定位内存泄漏?

答:有。

在内存泄漏情况复杂的时候,你可以用以下方法定位内存泄漏。这是我个人认为通用的内存泄漏追踪方法中最有效的手段。

我们再回头看看crtdbg生成的内存泄漏报告:

Detected memory leaks!

Dumping objects ->

c:/work/test.cpp(186) : {52} normal block at 0×003C4410, 40
bytes long.

Data:
<               
> CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

Object dump complete.

除了产生该内存泄漏的内存分配语句所在的文件名、行号为,我们注意到有一个比较陌生的信息:{52}。这个整数值代表了什么意思呢?

其实,它代表了第几次内存分配操作。象这个例子,{52}代表了第52次内存分配操作发生了泄漏。你可能要说,我只new过一次,怎么会是第52次?这很容易理解,其他的内存申请操作在C的初始化过程调用的呗。:)

有没有可能,我们让程序运行到第52次内存分配操作的时候,自动停下来,进入调试状态?所幸,crtdbg确实提供了这样的函数:即
long _CrtSetBreakAlloc(long nAllocID)。我们加上它:

inline void EnableMemLeakCheck()

{

_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
| _CRTDBG_LEAK_CHECK_DF);

}

#ifdef _DEBUG

#define new   new(_NORMAL_BLOCK, __FILE__,
__LINE__)

#endif

void main()

{

EnableMemLeakCheck();

_CrtSetBreakAlloc(52);

int* leak = new int[10];

}

你发现,程序运行到 int* leak = new int[10];
一句时,自动停下来进入调试状态。细细体会一下,你可以发现,这种方式你获得的信息远比在程序退出时获得文件名及行号有价值得多。因为报告泄漏文件名及行号,你获得的只是静态的信息,然而_CrtSetBreakAlloc则是把整个现场恢复,你可以通过对函数调用栈分析(我发现很多人不习惯看函数调用栈,如果你属于这种情况,我强烈推荐你去补上这一课,因为它太重要了)以及其他在线调试技巧,来分析产生内存泄漏的原因。通常情况下,这种分析方法可以在
5分钟内找到肇事者。

当然,_CrtSetBreakAlloc要求你的程序执行过程是可还原的(多次执行过程的内存分配顺序不会发生变化)。这个假设在多数情况下成立。不过,在多线程的情况下,这一点有时难以保证。

时间: 2024-10-11 07:10:32

最快速度找到内存泄漏[转载]的相关文章

(转)最快速度找到内存泄漏

最快速度找到内存泄漏 许式伟 2006年11月某日 内存管理是C++程序员的痛.我的<内存管理变革>系列就是试图讨论更为有效的内存管理方式,以杜绝(或减少)内存泄漏,减轻C++程序员的负担.由于工作忙的缘故,这个系列目前未完,暂停. 这篇短文我想换个方式,讨论一下如何以最快的速度找到内存泄漏. 1.确认是否存在内存泄漏 我们知道,MFC程序如果检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏.例如: class CMyApp : public CWinApp { public: BO

最快速度找到内存泄漏

确认是否存在内存泄漏 我们知道,MFC程序如果检测到存在内存泄漏,退出程序的时候会在调试窗口提醒内存泄漏.例如: class CMyApp : public CWinApp{public:   BOOL InitApplication()   {       int* leak = new int[10];       return TRUE;   }}; 产生的内存泄漏报告大体如下: Detected memory leaks!Dumping objects ->c:worktest.cpp(

教程2:如何找到内存泄漏dotmemory

在本教程中,我们将看到如何使用dotmemory定位和固定在你的应用程序的内存泄漏.但在开始之前,让我们在一个内存泄漏是一致的. 内存泄漏是什么? 根据维基百科,内存泄漏是由于不正确的内存管理时,”一个对象是存储在内存中,但不能被运行代码访问.”此外,“内存泄漏加起来的时间,如果他们不清理,系统最终耗尽内存.” 实际上,如果我们将严格按照上述定义,“经典”的内存泄漏是不可能的网络应用程序.垃圾收集器(GC)完全控制内存释放并删除所有的对象不能被访问的代码.此外,在应用程序关闭时,GC完全释放由应

Java 理论与实践: 用弱引用堵住内存泄漏---转载

要让垃圾收集(GC)回收程序不再使用的对象,对象的逻辑 生命周期(应用程序使用它的时间)和对该对象拥有的引用的实际 生命周期必须是相同的.在大多数时候,好的软件工程技术保证这是自动实现的,不用我们对对象生命周期问题花费过多心思.但是偶尔我们会创建一个引用,它在内存中包含对象的时间比我们预期的要长得多,这种情况称为无意识的对象保留(unintentional object retention). 全局 Map 造成的内存泄漏 无意识对象保留最常见的原因是使用 Map 将元数据与临时对象(trans

MS 这两项功能有助于找到内存泄漏的操作行为nalysis Tools (MA

可以将当前的内存 Dump成一个 hprof格式的文件,MAT 读取这个文件后会给出方便阅读的信息,配合它的查找,对比功能,就可以定位内存泄漏的原因. http://bbs.21ic.com/icview-1938146-1-1.htmlhttp://bbs.21ic.com/icview-1938148-1-1.htmlhttp://bbs.21ic.com/icview-1938150-1-1.htmlhttp://bbs.21ic.com/icview-1938152-1-1.htmlht

关于js闭包是否真的会造成内存泄漏(转载)

闭包是一个非常强大的特性,但人们对其也有诸多无解.一种危言耸听的说法是闭包会造成内存泄露. 局部变量本来应该在函数退出的时候被解除引用,但如果局部变量被封闭在闭包形成的环境中,那么这个局部变量就能一直生存下去.从这个意义上看,闭包的确会使一些数据无法被及时销毁.使用闭包的一部分原因是我们选择主动把一些变量封存在闭包中,因为可能在以后还需要使用这些变量,把这些变量放在闭包中和放在全局作用域,对内存方面的影响是一致的,这里并不能说成是内存泄露.如果在将来需要回收这些变量,我们可以手动把这些变量设为n

Facebook 的 iOS 内存泄漏监测自动化实践

内存是移动设备上的共享资源,如果一个 App 无法正确地进行内存管理的话,将会导致内存消耗殆尽,闪退以及性能的严重下降. Facebook 的 iOS 版本的许多功能模块共用了同一份内存空间,如果其中的某一个模块消耗了特别多的内存资源的话,将会对整个 App 造成严重影响.举个栗子,当某个功能模块不小心造成了内存泄漏的时候,这个情况就很有可能会发生. 在 Facebook,我们有非常多的工程师同时在一个代码仓库下进行并行开发.内存泄漏是在开发过程中难以避免会遇见的问题.当内存泄漏发生时,我们就需

java内存泄漏查找

java由于拥有自动垃圾回收机制,所以一般情况下,我们不需要考虑内存泄漏的问题.jvm会自动收回无用的对象.所谓无用的对象,表示你的程序不可能再访问的对象. 但是,有一种情况必须考虑,就是要防止容器(List,Map等)内的对象无限增大.因为对象存贮在容器中,会被容器引用,从而如果容器的有效,容器内的对象就不会释放. 一旦真的出现内存泄漏,随着时间的推移,java程序逐渐增大内存消耗,最后出现OutOfMemory异常而终止. 如何查找内存泄漏? 这种情况下,一般需要找到内存泄漏的对象.也就是要

android内存泄漏检测StrictMode和MAT工具使用

StrictMode说明 Android 2.3提供一个称为严苛模式(StrictMode)的调试特性,Google称该特性已经使数百个Android上的Google应用程序受益.那它都做什么呢?它将报告与线程及虚拟机相关的策略违例.一旦检测到策略违例(policy violation),你将获得警告,其包含了一个栈trace显示你的应用在何处发生违例.你可以强制用警告代替崩溃(crash),也可以仅将警告计入日志,让你的应用继续执行.策略的细节尚难确定,可以期待随Android的成熟Googl