6

我目前正在从事一个大型应用程序项目(用 c++ 编写),该项目是前一段时间从头开始的,我们已经到了必须对内存泄漏进行综合检查的地步。

该应用程序在 Ubuntu Linux 上运行,具有大量多媒体内容,并使用 OpenGl、SDL 和 ffmpeg 用于各种用途,包括 3D 图形渲染、窗口、音频和电影播放。您可以将其视为视频游戏,尽管它不是,但应用程序的职责可以通过将其视为视频游戏来简化。

我目前在确定我们是否仍然存在内存泄漏方面有点无能为力。过去我们已经识别了一些,并删除了它们。不过,这些天来,应用程序几乎完成了,我们运行的测试给了我我无法完全弄清楚的结果。

我做的第一件事是尝试通过 Valgrind 运行应用程序......不幸的是,在 valgrind 环境中运行时应用程序崩溃。“非确定性”的崩溃,因为它在不同的地方崩溃。所以我放弃了使用 Valgrind 来轻松识别潜在泄漏的来源,并最终使用了两个 Linux 命令:free 和 top。

free 用于在应用程序运行时探测系统内存使用情况

top 与“-p”选项一起使用,以在运行时探测应用程序进程的内存使用情况。

输出表单 top 和 free 被转储到文件中以进行后处理。我用问题底部链接的数据制作了两个图表。

测试用例非常简单:一旦应用程序已经启动并等待命令,就会探测有关内存的数据。然后我启动一系列重复执行相同操作的命令。该应用程序预计会将大量多媒体数据加载到 RAM 中,然后下载。

不幸的是,图表并没有向我展示我所期望的。内存使用量通过 3 个不同的步骤增长,然后停止。内存显然从未释放,这暗示我存在巨大的内存泄漏。那会很好,因为这意味着我们很可能没有释放被媒体占用的内存。

但是在前三个步骤之后......内存使用稳定......没有任何更大的步骤......只是轻微的上下移动,对应于预期的数据加载和卸载。这里出乎意料的是,应该加载/卸载的数据占 RAM 的百分之一兆字节,而不是上升和下降仅占几兆字节(比如说 8-10 MB)。

我目前对解释这些数据一无所知。

有人有一些提示或建议吗?我错过了什么?我用来检查是否存在宏观内存泄漏的方法是否完全错误?你知道除了 Valgrind 之外的任何其他(最好是免费的)工具来检查内存泄漏吗?

系统内存使用图

进程内存使用图

4

7 回答 7

5

与其放弃 Valgrind,不如与他们合作并尝试

  • 摆脱你在 Valgrind 中遇到的错误
  • 使用更新的 Valgrind 对您的应用进行全面测试和调试。

说你放弃了 Valgrind 这是解决你问题的方法并没有真正帮助......

Valgrind 是我们都用来检查 linux 下的内存泄漏和线程问题的工具。

最后,最好花时间弄清楚“为什么 Valgrind 不能与我的应用程序一起使用”,而不是寻找替代解决方案。Valgrind 是一个经过验证和测试的工具,但并不完美。它远远超过了替代方法。

Valgrind 页面说最好将错误提交给 Bugzilla,但实际上最好在https://lists.sourceforge.net/lists/listinfo/valgrind-users上询问是否有人以前看到过此类问题以及在这样的情况下该怎么做情况。最坏的情况——他们会告诉你向 bugzilla 提交错误或自己提交。

于 2012-12-29T17:34:37.763 回答
3

首先...

我们已经到了必须对内存泄漏进行汇总检查的地步。

这其实是方法论的问题。正确性应该是任何软件的主要目标,而不是事后的想法。

我会假设您现在意识到了这一点,并且如果您在每次提交时都运行检测单元测试,那么识别问题会容易得多。


那么,现在该怎么办?

  • 运行时检测:

    • 尝试让 Valgrind 工作,你可能有一些环境问题
    • 尝试ASanThreadSanMemSan;在 Linux 下设置它们并非易事,但令人印象深刻!
    • 尝试检测构建:例如tcmalloc包括一个堆检查器
    • ...
  • 编译时检测:

    • 打开警告(最好使用-Werror)(不是特定于您的问题)
    • 使用静态分析,例如Clang 的,它可能会发现不成对的分配例程
    • ...
  • 人体检测:

    • 代码审查:确保所有资源都分配在 RAII 类中
    • ...

注意:仅使用 RAII 类有助于消除内存泄漏,但无助于悬空引用。值得庆幸的是,检测悬空引用是 ASan 所做的。


一旦您修补了所有问题,请确保这成为该过程的一部分。更改应始终进行审查和测试,以便立即剔除臭鸡蛋,而不是让代码库发臭。

于 2012-12-29T19:34:57.750 回答
2

你可能想看看valgrind

您可能只想从非常简单的示例开始,以了解 valgrind 报告的内容可能有些冗长。考虑这个简化的例子,其中 valgrind 究竟缺少了什么和多少:

edd@max:/tmp$ cat valgrindex.cpp 

#include <cstdlib>

int main() {
  double *a = new double[100];
  exit(0);
}
edd@max:/tmp$ g++ -o valgrindex valgrindex.cpp 
edd@max:/tmp$ valgrind ./valgrindex
==15910== Memcheck, a memory error detector
==15910== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==15910== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==15910== Command: ./valgrindex
==15910== 
==15910== 
==15910== HEAP SUMMARY:
==15910==     in use at exit: 800 bytes in 1 blocks
==15910==   total heap usage: 1 allocs, 0 frees, 800 bytes allocated
==15910== 
==15910== LEAK SUMMARY:
==15910==    definitely lost: 0 bytes in 0 blocks
==15910==    indirectly lost: 0 bytes in 0 blocks
==15910==      possibly lost: 0 bytes in 0 blocks
==15910==    still reachable: 800 bytes in 1 blocks
==15910==         suppressed: 0 bytes in 0 blocks
==15910== Rerun with --leak-check=full to see details of leaked memory
==15910== 
==15910== For counts of detected and suppressed errors, rerun with: -v
==15910== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
edd@max:/tmp$ 
于 2012-12-29T17:28:36.933 回答
2

结果来自free并且top不会对您有帮助。我很遗憾你努力为他们的结果构建图表。我已经很好地解释了为什么它们在此处的类似主题中没有帮助:Linux 中 C++ 应用程序的内存稳定性

我也会同意这里的其他答案,您可能应该优先解决您在 Valgrind 中遇到的崩溃问题。Valgrind 在这一点上被认为非常稳定,我个人运行过相当复杂的多线程多媒体 SDL/OpenGL/等。应用程序通过它没有问题。Valgrind 的运行环境更有可能暴露您的应用程序中可能存在的不稳定性。崩溃听起来像是线程竞争条件崩溃,尽管它也可能是堆/内存损坏。

那么,您可能想要询问的是有关如何调试在 Valgrind 的运行环境中崩溃的应用程序的建议(我不知道答案)。

于 2012-12-29T18:10:17.627 回答
2

free 和 top 的问题是它们可以向您显示问题,但它们在解决问题方面几乎没有帮助。在分配内存的 100 行或 1000 行代码中,哪些是泄漏的?这就是 valgrind 提供帮助的地方。

如果这是针对工具预算有限的公司,您可能会考虑 purify 或其他商业工具。

为了完整起见,我将提到 Boehm 保守的垃圾收集内存分配器(适用于 C 和 C++ 代码)。您可以关闭 GC 并使用 GC_Free() 它成为泄漏检测工具。或者,您可以启用 GC 以在不再使用时自动释放内存。

于 2012-12-29T18:15:52.703 回答
0

这完全取决于您使用的分配器。libc 分配器(malloc、calloc、realloc)和 C++ 分配器(new、delete)可能使用了不将内存释放回操作系统的优化技巧。你看,如果你向 malloc 请求一些内存,使用它,然后释放它,它不一定会被释放回操作系统。相反,如果我向 malloc 请求内存,那么(大多数莉莉)获得更多的内存是必要的(因为页面边界)。这样,下次您需要更多内存时,malloc 就可以将其放在一边。免费也一样。内存可能只是添加到 mallocs 内存池中,您以后的分配正在从中提取。

因此,您的应用程序最初的几个 malloc 会将内存非常高,但随后池足够大以容纳未来的分配。

于 2012-12-29T17:41:01.253 回答
0

除了使用valgrind您还可以考虑使用Boehm 的保守 GC;您可能想要编译它并将其配置为内存泄漏检测器。

您甚至可能敢于使用 Boehm 的 GC 作为您的主要内存分配器。

顺便说一句,调查/proc/1234/maps可以帮助您(其中 1234 是您的进程的 pid)。

于 2012-12-29T20:03:36.150 回答