内存泄漏 - 每个程序员的恐怖?

时间:2011-02-06 15:27:18

标签: c++ memory-leaks lua

我正在用C ++编写一个游戏引擎,它也支持Lua。

我最大的恐怖:内存泄漏。

这并不像我的游戏已经被他们感染了,我很害怕它们像蘑菇一样从地面冒出来,当开发处于后期阶段并且项目庞大而复杂时。

我害怕他们,因为他们似乎很难找到我。尤其是在复杂系统中。如果我的引擎差不多完成,游戏就会运行,内存就会消失,我该怎么办?我将从哪里开始搜索?

  • 我对内存泄漏的恐惧是否合理?
  • 如何找出内存泄漏的位置?
  • 今天是不是有很好的工具可以帮助找到内存泄漏的来源?

12 个答案:

答案 0 :(得分:36)

  

如何找出内存泄漏的位置?

Valgrind

答案 1 :(得分:17)

原始指针只是导致内存泄漏的一个潜在原因。

即使您使用智能指针(如shared_ptr),如果您有一个循环,也可能会出现泄漏 - 解决此问题的方法是在某处使用weak_ptr来打破循环。使用智能指针并不能解决内存泄漏问题。

您还可以忘记基类中的虚拟析构函数,并以此方式获取泄漏。

即使没有删除新ed对象也没有问题,由于地址空间碎片,长时间运行的进程可能会增长(并且似乎会泄漏)。

像valgrind这样的工具对于查找泄漏非常非常有用,但它们并不总能告诉你修复的位置(例如,在循环或对象保持智能指针的情况下)

答案 2 :(得分:13)

需要一个定义良好的“对象生命周期”模型。无论何时你做“新”,你都需要考虑

  1. 谁拥有这个堆对象?即谁负责维护此指针并允许其他“客户”引用它?

  2. 谁负责删除此对象?这通常是#1,但不一定。

  3. 此对象的客户端的生命周期是否长于此对象的客户端?如果这是真的,并且它们实际上正在存储这个堆指针,它将取消引用不再“存在”的内存。您可能需要添加一些通知机制或重新设计“对象生命周期”模型。

  4. 很多时候你修复了内存泄漏问题,但后来遇到问题#3。这就是为什么在编写太多代码之前最好考虑一下对象生命周期模型。

答案 3 :(得分:10)

你从不使用原始指针来对抗内存泄漏。如果你有使用原始指针的代码,重构。

答案 4 :(得分:7)

答案 5 :(得分:6)

AFAIK Valgrid只是Linux 对于Windows,您有BoundsCheckerPurify等工具 如果您使用的是Visual Studio,那么C运行时库(CRT)也提供了一个非常简单且有用的工具,可用于查找开箱即用的内存泄漏。阅读_CrtDumpMemoryLeaks及其相关函数和宏 它基本上允许您在进程退出时获取内存泄漏的索引转储,然后允许您在分配泄漏内存的时间设置断点,以确切了解它何时发生。这与大多数其他工具形成鲜明对比,这些工具只能为您提供事后分析,而无法重现导致内存泄漏的事件。
从第一天开始使用这些小宝石可以让您相对安心,保持良好的状态。

答案 6 :(得分:4)

  

我对内存泄漏的恐惧是否合理?

如果您正在编写包含它们的代码,那么绝对是。

  

如何找出内存泄漏的位置?

通过分析代码。

  

今天是不是有很好的工具可以帮助找到内存泄漏的来源?

是的,但这仍然不容易。

好消息是,通过适当的设计完全没有必要。一盎司的预防值得大量治疗:处理内存泄漏的最佳策略是以保证不存在任何内容的方式编写代码。

答案 7 :(得分:3)

冒着听起来像我可能是自鸣得意的混蛋的风险,考虑使用 1979之后开发的任何编程语言,它没有内存泄漏问题,堆损坏堆栈损坏,甚至内存管理。 (任何说“我需要我的程序要快”的人可能从未听说过Donald Knuth。)

答案 8 :(得分:2)

内存泄漏并不太可怕 - 但它们可能对程序的性能有害(所以摆脱它们!)。

  1. 不要害怕内存泄漏。想想它们是什么 - 内存没有被删除但是所有访问都被删除(因此在执行结束之前,系统不知道它可以重新分配该内存)。
  2. 您可以“手动”找到内存泄漏,可以通过浏览对象并确保它们在适当的位置被删除(只有一次!否则会出现其他错误)。 Valgrind等工具可以帮助找出错误的位置。
  3. 正如前面提到过的人(我在上面提到过)Valgrind是查找内存泄漏的好工具。像这样运行:
  4. valgrind --leak-check=full -v ./YOUR_EXECUTABLE

    这将为您提供关于程序中内存使用方式的完整泄漏检查和详细(-v)输出。

    的问候,
    丹尼斯M。

答案 9 :(得分:2)

至少对于Lua部分,您可以使用自己的内存分配器并跟踪所有分配和释放,从而发现任何内存泄漏。

答案 10 :(得分:1)

有各种跟踪内存泄漏的技术。

最简单的方法是使用一个宏和一个特定的分配器来存储分配它的函数。这样,您可以跟踪每个分配,并查看哪些分配不应该删除。然后你可以开始编写unittest并断言已经释放了内存。

如果您一直使用预编译的容器,这将不起作用,因为所有分配都将在容器中。那你的选择是:

  • 使用thread-local-value来标识正在运行的子系统或类id(在调试版本中),以便分配器可以检测谁在分配内存。您甚至可以使用堆栈在引擎中分层跟踪内存使用情况。
  • 实际上检索调用堆栈并存储,如果您的编译器有足够的支持。
  • 为子系统使用内存池,并测量它们的大小是否不成比例地增加。 (这也是泄漏内存的一种(不可否认的)解决方法,因为你可以立即释放整个池,从而释放泄漏的内存,如果你能够的话。)
  • 在Windows上,有一些宏可以在调试版本下自动跟踪源代码行的内存分配。

可能有更多选择。如果您的设计允许,测试和使用自定义全局新/删除覆盖(可以查询)应该是有用的。

另外,请参阅Electronic Arts STL C++ paper,了解在STL / C ++中需要做些什么来支持正确的游戏开发。 (它可能比你的引擎更硬一点,但它肯定包含许多灵感和独创性。)

答案 11 :(得分:1)

  

如何找出内存泄漏的位置?

Visual Leak Detector for Visual C ++ 2008/2010