5

我想验证我为 Linux 编写和编译的 C++ 应用程序的内存稳定性。它是一个网络应用程序,以每秒 10-20 个连接的速率响应远程客户端的连接。从长远来看,内存上升到 50MB,尽管应用程序正在调用删除......

调查表明,Linux 不会立即释放内存。所以这是我的问题:

如何强制 Linux 释放我实际释放的内存?至少我想这样做一次以验证内存稳定性。否则,是否有任何可靠的内存指示器可以报告我的应用程序实际持有的内存?

4

6 回答 6

5

您所看到的很可能根本不是内存泄漏。如今,操作系统和 malloc/new 堆都对内存进行了非常复杂的计算。总的来说,这是一件非常好的事情。 您尝试强制操作系统释放内存的任何尝试都只会损害您的应用程序性能和整体系统性能。

为了显示:

  1. 堆保留几个虚拟内存区域供使用。在 malloc 之前,实际上都没有提交(由物理内存支持)。

  2. 你分配内存。堆相应地增长。您可以在任务管理器中看到这一点。

  3. 您在堆上分配更多内存。它长得更多。

  4. 您释放了在第 2 步中分配的内存。但是,堆无法收缩,因为仍然分配了 #3 中的内存,并且堆无法压缩内存(这会使您的指针无效)。

  5. 你 malloc/new 更多的东西。这可能会在步骤 #3 中分配内存后被添加,因为它无法放入由释放 #2 所打开的区域,或者因为堆管理器在堆中搜索 # 所打开的块的效率低下2. (取决于堆实现和正在分配/释放的内存块大小)

那么,第 2 步的那段记忆现在对世界来说已经死了吗?不必要。一方面,一旦它变得有效,它最终可能会被重用。在不被重用的情况下,操作系统本身可能能够使用 CPU 的虚拟内存功能 (TLB) 从您的应用程序下“重新映射”未使用的内存,并将其分配给另一个应用程序 - on苍蝇。堆意识到了这一点,并且通常以某种方式管理事物以帮助提高操作系统重新映射页面的能力。

这些是有价值的内存管理技术,它们具有通过 Process Explorer 渲染细粒度内存泄漏检测的明显副作用,大多数情况下是无用的。 如果要检测堆中的小内存泄漏,则需要使用运行时堆泄漏检测工具。既然你提到你也可以在 Windows 上构建,我会注意到微软的 CRT 内置了足够的泄漏检查工具。在此处找到使用说明:

http://msdn.microsoft.com/en-us/library/974tc9t1(v=vs.100).aspx

也有用于 GCC/Clang 工具链的 malloc 的开源替代品,尽管我没有直接使用它们的经验。我认为在 Linux 上Valgrind无论如何都是首选且更可靠的泄漏检测方法。(根据我的经验,比 MSVCRT Debug 更容易使用)。

于 2012-12-24T19:01:21.153 回答
5

我建议将 valgrind 与 memcheck 工具或任何其他分析工具一起用于内存泄漏

来自 Valgrind 的页面:

内存检查

检测内存管理问题,主要针对 C 和 C++ 程序。当一个程序在 Memcheck 的监督下运行时,所有的内存读写都会被检查,并且对 malloc/new/free/delete 的调用会被拦截。因此,Memcheck 可以检测您的程序是否:

  • 访问它不应该访问的内存(尚未分配的区域、已释放的区域、堆块末尾的区域、堆栈的不可访问区域)。
  • 以危险的方式使用未初始化的值。
  • 泄漏内存。
  • 堆块的错误释放(双重释放,不匹配的释放)。
  • 将重叠的源和目标内存块传递给 memcpy() 和相关函数。

Memcheck 会在这些错误发生时立即报告,并给出发生错误的源代码行号,以及为到达该行而调用的函数的堆栈跟踪。Memcheck 在字节级别跟踪可寻址性,并在位级别跟踪值的初始化。因此,它可以检测单个未初始化位的使用,并且不会报告位域操作的虚假错误。Memcheck 运行程序的速度比正常速度慢 10--30 倍。缓存研磨

地块

Massif 是一个堆分析器。它通过定期拍摄程序堆的快照来执行详细的堆分析。它会生成一个图表,显示随时间的堆使用情况,包括有关程序的哪些部分负责最多内存分配的信息。该图由文本或 HTML 文件补充,其中包含用于确定分配最多内存的位置的更多信息。Massif 运行程序的速度比正常速度慢 20 倍。

使用 valgrind 就像使用所需的开关运行应用程序并将其作为 valgrind 的输入一样简单:

valgrind --tool=memcheck ./myapplication -f foo -b bar
于 2012-12-24T16:55:21.500 回答
2

我非常怀疑,除了用另一个函数包装mallocfree [or newand delete] 之外的任何东西实际上都可以让你得到除了非常粗略的估计之外的任何东西。

问题之一是释放的内存只有在有很长的连续内存块时才能释放。通常发生的情况是整个堆都使用了“少量”内存,并且您找不到可以释放的大块。

您不太可能以任何简单的方式解决此问题。

顺便说一句,当您再次加载更多负载时,您的应用程序可能会需要这 50MB,因此释放它只是浪费精力。

(如果您不使用的内存用于其他用途,它将被换出,并且长时间未触及的页面是主要候选者,因此如果系统在执行某些其他任务时内存不足,它仍然会为该空间重用您机器中的 RAM,因此它不会浪费在那儿 - 只是您不能使用 'ps' 或诸如此类的东西来确定程序使用了多少内存!)

正如评论中所建议的:您还可以编写自己的内存分配器,mmap()用于创建一个“块”来分配部分。如果你有一段代码做了很多内存分配,然后所有这些肯定会在以后被释放,从一个单独的内存块中分配所有这些,当它全部被释放时,你可以把 mmap' d 区域回到“空闲mmap列表”,当列表足够大时,释放一些mmap分配[这是为了避免调用 mmap LOTS 多次,然后munmap几毫秒后再次]。但是,如果您曾经让其中一个内存分配“逃逸”出您的围栏区域,您的应用程序可能会崩溃(或者更糟糕的是,不会崩溃,而是使用属于应用程序其他部分的内存,您会得到一个非常某处奇怪的结果,例如一个用户看到了应该为另一个用户提供的网络内容!)

于 2012-12-24T16:56:09.170 回答
1

free当您调用或时,操作系统通常不会释放内存delete。此内存返回运行时库中的堆管理器。

如果你想真正释放内存,你可以使用brk. 但这会打开一大堆内存管理蠕虫。如果你直接打电话brk,你最好不要打电话malloc。对于 C++,您可以覆盖直接new使用brk

这不是一件容易的事。

于 2012-12-24T16:52:56.880 回答
1

最新的 dlmalloc() 有一个称为 mspace 的概念(其他人称之为区域)。您可以针对 mspace 调用 malloc() 和 free()。或者您可以删除 mspace 以立即释放从 mspace 分配的所有内存。删除 mspace 将从进程中释放内存。

如果您创建一个带有连接的 mspace,从该 mspace 为该连接分配所有内存,并在连接关闭时删除该 mspace,您将不会有进程增长。

如果您在一个 mspace 中有一个指针指向另一个 mspace 中的内存,并且您删除了第二个 mspace,那么正如语言律师所说“结果未定义”。

于 2012-12-24T17:45:39.230 回答
1

使用 valgrind 查找内存泄漏:valgrind ./your_application

它将列出您分配内存的位置并且没有释放它。

我认为这不是 linux 问题,而是在您的应用程序中。如果您使用 «top» 监控内存使用情况,您将无法获得非常精确的使用情况。尝试使用 massif(valgrind 的工具): valgrind --tool=massif ./your_application 以了解实际内存使用情况。

作为避免 C++ 泄漏的更一般规则:使用智能指针而不是普通指针。同样在许多情况下,您可以使用 RAII (http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) 而不是使用“new”分配内存。

于 2012-12-24T16:51:04.300 回答