如何在C ++代码/项目中查找内存泄漏?

我是Windows平台上的C ++程序员。 我正在使用Visual Studio 2008。

我通常最终在内存泄漏代码。

通常我通过检查代码来发现内存泄漏,但是这很麻烦,并不总是一个好方法。

由于我买不起付费内存泄漏检测工具,所以我想让大家build议最好的方法来避免内存泄漏。

  1. 我想知道程序员如何find内存泄漏。
  2. 是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏?

说明

你需要的东西

  • 熟练使用C ++
  • C ++编译器
  • debugging器和其他调查软件工具

1

了解操作员的基本知识。 C ++运算符“new”分配堆内存。 “删除”运算符释放堆内存。 对于每一个“新”,你应该使用“删除”,这样你释放你分配的相同的内存:

char* str = new char [30]; // Allocate 30 bytes to house a string. delete [] str; // Clear those 30 bytes and make str point nowhere. 

2

只有在删除后才能重新分配内存。 在下面的代码中,str通过第二次分配获取一个新地址。 第一个地址不可挽回地丢失了,它指向的30个字节也是如此。 现在他们不可能自由,而且你有内存泄漏:

 char* str = new char [30]; // Give str a memory address. // delete [] str; // Remove the first comment marking in this line to correct. str = new char [60]; /* Give str another memory address with the first one gone forever.*/ delete [] str; // This deletes the 60 bytes, not just the first 30. 

3

观看这些指针分配。 每个dynamicvariables(堆中分配的内存)都需要与一个指针相关联。 当一个dynamicvariables与其指针分离时,就不可能擦除。 同样,这导致内存泄漏:

 char* str1 = new char [30]; char* str2 = new char [40]; strcpy(str1, "Memory leak"); str2 = str1; // Bad! Now the 40 bytes are impossible to free. delete [] str2; // This deletes the 30 bytes. delete [] str1; // Possible access violation. What a disaster! 

4

请注意本地指针。 你在一个函数中声明的指针被分配到堆栈上,但它指向的dynamicvariables被分配到堆上。 如果您不删除它,程序退出该function后,它将保留:

 void Leak(int x){ char* p = new char [x]; // delete [] p; // Remove the first comment marking to correct. } 

“删除”后请注意方括号。 使用“删除”本身来释放一个对象。 使用“删除”[]与方括号来释放堆数组。 不要这样做:

 char* one = new char; delete [] one; // Wrong char* many = new char [30]; delete many; // Wrong! 

6

如果泄漏仍然允许 – 我通常与delecker寻找(在这里检查: http ://deleaker.com)。

谢谢!

您可以在代码中使用一些技术来检测内存泄漏。 最常见和最简单的检测方法是,定义一个macrosDEBUG_NEW,并使用它,以及__FILE____LINE__这样的预定义macros来定位代码中的内存泄漏。 这些预定义的macros告诉你内存泄漏的文件和行号。

DEBUG_NEW只是一个MACRO,通常定义为:

 #define DEBUG_NEW new(__FILE__, __LINE__) #define new DEBUG_NEW 

所以,无论你使用new ,它也可以跟踪文件和行号,可以用来定位内存泄漏在你的程序。

__FILE____FILE__ __LINE__是预定义的macros ,它们分别在文件名和行号处使用它们!

阅读下面的文章,其中解释了与其他有趣的macros使用DEBUG_NEW技术,非常漂亮:

跨平台的内存泄漏检测器


从维基百科 ,

Debug_new引用C ++中的一种技术来重载和/或重新定义操作符new和operator delete,以拦截内存分配和释放调用,从而debugging程序以用于内存使用。 它通常涉及到定义一个名为DEBUG_NEW的macros,并使新成为新的(_ FILE _,_ LINE _)来logging关于分配的文件/行信息。 Microsoft Visual C ++在其Microsoft基础类中使用此技术。 有一些方法可以扩展此方法,以避免使用macros重定义,同时仍然能够在某些平台上显示文件/行信息。 这种方法有许多固有的局限性。 它只适用于C ++,不能通过malloc等C函数捕获内存泄漏。 但是,与一些更完整的内存debugging器解决scheme相比,它可以非常简单易用,速度也非常快。

有一些众所周知的编程技术可以帮助您最大限度地减less第一手获取内存泄漏的风险:

  • 如果你必须做自己的dynamic内存分配,写成newdelete总是成对的,并确保分配/解除分配代码被称为成对
  • 如果可以的话,避免dynamic内存分配。 例如,尽可能使用vector<T> t而不是T* t = new T[size]
  • 使用像智能指针一样的“智能指针”( http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/smart_ptr.htm
  • 我个人最喜欢的:确保你已经理解了指针所有权的概念,并确保在你使用指针的任何地方,你知道哪个代码实体是所有者
  • 了解哪些构造函数/赋值运算符是由C ++编译器自动创build的,以及如果您拥有拥有指针的类(或者如果您的类包含指向它不具有的对象的指针的类),这意味着什么。
  1. 下载Windowsdebugging工具 。
  2. 使用gflags实用程序打开用户模式堆栈跟踪。
  3. 使用UMDH获取程序内存的多个快照。 在分配内存之前拍摄一张快照,并在您认为程序泄漏内存的地方之后拍摄第二张快照。 您可能希望在程序中添加暂停或提示,让您有机会运行UMDH并拍摄快照。
  4. 再次运行UMDH ,这次是在两个快照之间做差异的模式。 然后它将生成一个包含可疑内存泄漏调用堆栈的报告。
  5. 完成后恢复以前的gflags设置。

UMDH会给你比CRTdebugging堆更多的信息,因为它正在监视整个过程的内存分配; 它甚至可以告诉你,如果第三方组件泄漏。

如果你使用gcc,可以使用gprof。

我想知道程序员如何发现内存泄漏

有些使用工具,有些使用工具,也可以通过同行代码审查

是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏

对我来说:每当我创builddynamic分配的对象,我总是把释放的代码之后,然后填写代码之间。 如果你确信代码之间不会有例外,那么这样做可以。 否则,我使用try-finally(我不经常使用C ++)。

  1. 在Visual Studio中,有一个内置的内存泄漏检测器,称为C运行时库。 当主函数返回后程序退出时,CRT将检查应用程序的debugging堆。 如果你仍然有任何块在debugging堆上分配,那么你有内存泄漏..

  2. 本论坛讨论了一些避免C / C ++内存泄漏的方法。

在你的代码中searchnew出现,并确保它们都出现在析构函数中具有匹配删除的构造函数中。 确保这是该构造函数中唯一可能的引发操作。 一个简单的方法是将所有的指针包装在std::auto_ptrboost::scoped_ptr (取决于你是否需要移动语义)。 对于所有将来的代码,只要确保每个资源都由一个对象所拥有,这个对象将在其析构函数中清理资源。 如果你需要移动语义,那么你可以升级到一个支持r值引用的编译器(VS2010我相信)并创build移动构造函数。 如果你不想这样做,那么你可以使用各种棘手的技巧,包括认真使用交换,或者尝试Boost.Move库。

在Windows上,您可以使用CRTdebugging堆 。

是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏。

是的,不要使用手动内存pipe理(如果你曾经手动deletedelete[] ,那么你做错了)。 使用RAII和智能指针,将堆分配限制在绝对最小值(大部分时间,自动variables就足够了)。

回答你的问题的第二部分,

是否有任何标准或程序应该遵循,以确保程序中没有内存泄漏。

就在这里。 这是C和C ++之间的主要区别之一。

在C ++中,你不应该在你的用户代码中调用new或者delete 。 RAII是一种非常常用的技术,它几乎解决了资源pipe理问题。 程序中的每个资源(资源都是任何必须获取的资源,然后发布:文件句柄,networking套接字,数据库连接,还有纯内存分配,在某些情况下还包括一对API调用(BeginX )/ EndX(),LockY(),UnlockY())应该被包装在一个类中,其中:

  • 构造函数获取资源(如果资源是memroy分配,则通过调用new
  • 析构函数释放资源,
  • 可以防止复制和分配(通过使复制构造函数和赋值运算符为私有),或者实现正确工作(例如通过克隆底层资源)

这个类然后在本地,堆栈或类成员实例化,而不是通过调用new和存储指针。

你通常不需要自己定义这些类。 标准库容器的行为也是如此,所以当向量被销毁时,任何存储在std::vector对象都将被释放。 所以再次,不要将指针存储到容器(这将需要调用newdelete ),而是对象本身 (它给你免费的内存pipe理)。 同样,可以使用智能指针类轻松地包装刚刚分配给new ,并控制它们的生命周期。

这意味着当对象超出范围时,它会被自动销毁,并释放和清理其资源。

如果你在你的代码中一直这样做,你就不会有任何内存泄漏。 所有可能泄漏的东西都与一个析构函数绑定在一起,当控件离开声明对象的范围时,这个析构函数被保证被调用。

视觉泄漏检测器(Visual Leak Detector,VLD)是Visual C ++免费,强大的开源内存泄漏检测系统。

在Visual Studiodebugging器下运行程序时,Visual Leak Detector将在debugging会话结束时输出内存泄漏报告。 泄漏报告包括完整的调用堆栈,显示如何分配泄漏的内存块。 双击调用堆栈中的一行,跳转到编辑器窗口中的文件和行。

如果您只有崩溃转储,则可以使用Windbg !heap -l命令,它将检测泄漏的堆块。 最好打开gflags选项:“创build用户模式堆栈跟踪数据库”,然后你会看到内存分配调用堆栈。

MTuner是一个免费的多平台内存分析,泄漏检测和分析工具,支持MSVC,GCC和Clang编译器。 function包括:

  • 基于时间线的内存使用历史和实时内存块
  • 强大的内存操作过滤基于堆,内存标签,时间范围等
  • 完整源代码的手动testingSDK
  • 通过命令行使用持续集成支持
  • 调用堆栈树和树图导航
  • 多得多。

用户可以使用GCC或Clang交叉编译器来分析任何软件定位平台。 MTuner内置支持Windows,PlayStation 4和PlayStation 3平台。

运行“Valgrind”可以:

1) 帮助识别内存泄漏 – 告诉你有多less内存泄漏,并指出泄漏内存分配的代码行。

2) 指出释放内存的错误尝试 (例如,不正确的“删除”)

使用“Valgrind”的说明

1) 在这里获得valgrind。

1)用-g标志编译你的代码

3)在你的shell运行:

 valgrind --leak-check=yes myprog arg1 arg2 

“myprog”是你编译的程序,“arg1”,“arg2”是你程序的参数。

4)结果是一个malloc / new的调用列表,没有后续的免费删除调用。

例如:

 ==4230== at 0x1B977DD0: malloc (vg_replace_malloc.c:136) ==4230== by 0x804990F: main (example.c:6) 

告诉你在哪一行malloc(没有被释放)被调用。

正如其他人指出的那样,确保每一个“新”/“”malloc“调用你有一个后续的”删除“/”免费“的电话。

AddressSanitizer (ASan)是一个快速的内存错误检测器。 它发现在C / C ++程序中使用后释放和{堆,堆栈,全局}缓冲区溢出错误。 它发现:

  • 免费使用(悬挂指针解除引用)
  • 堆缓冲区溢出
  • 堆栈缓冲区溢出
  • 全局缓冲区溢出
  • 返回后使用
  • 初始化顺序错误

这个工具非常快。 仪器化程序的平均减速是〜2倍。

除了其他方法中提供的工具和方法之外,还可以使用静态代码分析工具来检测内存泄漏(以及其他问题)。 免费的一个强大的工具是Cppcheck。 但是还有很多其他工具可用。 维基百科有一个静态代码分析工具的列表。

这可能会帮助只想使用Visual Studio进行泄漏检测的人。 VS 2015及以上版本的“诊断工具”现在有了很大的改进。 也尝试过称为“Deleaker”的工具,但Visual Studio工具同样不错。 看下面的video帮助我开始了它。

https://www.youtube.com/watch?v=HUZW8m_3XvE