如何调试奇怪的内存泄漏(C++)

3
我正在编写一个Linux守护程序,目前看起来工作得很好,但是它会泄漏内存(这很糟糕 - 几个小时后,它将使用系统内存的60%之后导致段错误)。奇怪的是,我只使用了new/delete操作符并在主函数周围有try/catch块,因此不是由new引发的异常 - 只是由于缺乏内存而在某些时候导致段错误。我已经使用了valgrind,但它只找到了一次小泄漏和其他什么都没有。我也尝试过gdb,但尽管应用程序已经使用-g -rdynamic标志进行编译,但它并未将所有地址转换为函数名称。你能告诉我一些更好的内存调试方法,以便确定泄漏的源吗?

3
分段错误通常不是由于内存不足引起的;它们发生是因为以错误的方式访问了内存(解除引用未初始化、空或已删除的指针)或管理不当(双重删除、delete[]/delete不匹配等)。故障点不总是出现在错误点,因为可能会破坏内存使程序后续失败。 - Tyler McHenry
8个回答

4

Valgrind通常非常可靠地发现内存泄漏,所以您确定这是内存泄漏吗?

堆分析器可以帮助您查看正在创建的对象以及它们是否符合您的预期。Massif是一种可能证明有用的工具。


3
在C++中,“内存泄漏”指的是孤立无援的内存,尽管它已经没有被使用,但却无法被释放。如果valgrind说你没有“泄漏”,那么你可能仍然保留了不再需要的对象指针。严格来说这不算是一种泄漏,但它会导致你的内存使用量不断膨胀。
如果想要使用valgrind,请传递--show-reachable标志,这将使其在应用程序退出时转储所有仍然存在于内存中的对象。然后您可以查看这些堆栈跟踪,并确定哪些对象在内存中保留时间过长以及原因。

0

正如其他人所指出的那样,这可能不是一个简单的泄漏。更有可能的是错误的强制转换,使其误解某个对象的类型。或者是缓冲区下溢/上溢,或者是整数翻转(一些32位有符号偏移超过2Gig?),或者是悬空指针....

根据我的经验,在构建具有各种指针的复杂结构时,您需要一个结构的正式规范。特别重要的是一组不变量。有了这些,构建一个测试套件和运行时验证器,可以确保您的数据仍然一致且整洁。


0

我建议你首先尝试使用gdb解决问题,因为它是发现segfault发生时发生了什么的#1工具。然后运行程序直到它崩溃,并确保系统生成核心文件(使用ulimit -c unlimited允许创建核心文件,如果它是一个守护进程,请注意必须为实际运行守护进程的用户执行此操作)。此时,您可以使用带有core文件的gdb查找segfault发生的位置(参见backtrack命令)。


0

这也可能是堆栈溢出(当然这很奇怪,但它也是符合你所有问题的可能性)。例如,如果一个函数在一段时间后调用自身(隐式或显式),就可能会发生这种情况。我再次强调,这将是一个奇怪而非常罕见的问题,但这是我在阅读所有答案和问题后想到的事情。


0

Valgrind有一个缺点,就是在多线程应用程序中查找泄漏可能会比较困难。如果您的泄漏是由竞争条件引起的,在调试期间它将消失,因为Valgrind会序列化所有内容。

我调试过的几乎每个“泄漏”,特别是在Java中,都是标准集合中无限增长的结果。例如:从不清理的向量、超出预期的字典等。如果Valgrind没有发现任何问题,那么很有可能这不是真正的泄漏。


但是,如果程序没有竞态条件,那会有问题吗?如果有,也许 OP 应该开始修复它。 - qdii
这不是问题,但有时在Valgrind中成功通过可以作为一个指标。单线程服务器除了性能方面不应该有任何副作用。 - pestilence669

0

你的编译器或链接器有没有出现任何奇怪的警告?与libstdc++有关的吗?我们曾经遇到过一个问题,即同时链接两个不同版本的libstdc++(5和6),这给我们带来了可怕的内存泄漏。


0

我曾经听说过,有时候如果内存不足,new操作符会返回NULL而不是抛出异常。也许这就导致了段错误?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接