Linux下c++应用程序的内存稳定性

Memory stability of a C++ application in Linux

本文关键字:内存 稳定性 应用程序 c++ Linux      更新时间:2023-10-16

我想验证我为Linux编写和编译的一个c++应用程序的内存稳定性。它是一个网络应用程序,以每秒10-20个连接的速率响应远程客户机连接。在长时间运行时,内存上升到50MB,即使应用程序正在调用删除…

调查显示Linux不会立即释放内存。下面是我的问题:

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

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

游戏介绍:

  1. 堆保留了几个虚拟内存区域供使用。在malloc之前,这些都不会被实际提交(由物理内存支持)

  2. 分配内存。堆相应地增长。

  3. 在堆上分配更多内存。

  4. 释放在步骤2中分配的内存。但是Heap不能收缩,因为#3中的内存仍然是分配的,而且Heap不能压缩内存(这会使指针失效)。

  5. 你malloc/new更多的东西。这可能会在步骤#3中分配的内存之后附加在上,因为它无法容纳释放#2留下的开放区域,或者因为对于堆管理器来说,在堆中搜索#2留下的开放块是低效的。(取决于堆实现和分配/释放的内存块大小)

那么,步骤2中的记忆现在对世界来说已经死了吗?不一定。首先,一旦它变得有效,它最终可能会被重用。在没有被重用的情况下,操作系统本身可能能够使用CPU的虚拟内存特性(TLB)将未使用的内存从应用程序下"重新映射"出来,并动态地将其分配给另一个应用程序。Heap意识到这一点,并且通常以一种有助于提高操作系统重新映射页面的能力的方式来管理事情。

这些都是很有价值的内存管理技术,它们具有明显的副作用,即通过Process Explorer进行细粒度内存泄漏检测几乎毫无用处。如果您想检测堆中的小内存泄漏,那么您需要使用运行时堆泄漏检测工具。既然您提到您也可以在Windows上构建,我要指出微软的CRT具有足够的内置泄漏检查工具。使用说明:

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

也有可以与GCC/Clang工具链一起使用的malloc的开源替代品,尽管我没有直接的经验。我认为在Linux 上,Valgrind是泄漏检测的首选和更可靠的方法。(在我的经验中比MSVCRT Debug更容易使用)。

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

from Valgrind's page:

Memcheck

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

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

Memcheck在这些错误发生时立即报告它们,并给出它们的来源的堆栈跟踪信息到达该行所调用的函数。Memcheck跟踪可寻址性字节级和位级值的初始化。作为一个结果,它可以检测到单个未初始化位的使用,并且确实如此不报告位域操作的虚假错误。Memcheck运行程序比正常慢10- 30倍。Cachegrind

地块

Massif是一个堆分析器。执行详细的堆分析对程序的堆进行定期快照。它会生成一个图形显示堆随时间的使用情况,包括关于哪些部分的信息的程序负责大部分的内存分配。的图形由包含更多内容的文本或HTML文件补充用于确定在何处分配了最多内存的信息。Massif运行程序的速度比正常情况慢20倍。

使用valgrind很简单,只需运行带有所需开关的应用程序,并将其作为valgrind的输入:

valgrind --tool=memcheck ./myapplication -f foo -b bar

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

其中一个问题是,释放的内存只能在有很长的连续内存块的情况下释放。通常发生的情况是,堆上有"小块"内存被使用,而您找不到可以释放的大块内存。

你不太可能用任何简单的方法解决这个问题。

顺便说一下,当你有更多的负载时,你的应用程序可能会需要这50MB,所以释放它只是浪费精力。

(如果您没有使用的内存被其他事情所需要,它将被交换出来,并且长时间未触及的页面是主要候选,因此,如果系统为某些其他任务运行内存不足,它仍然会重用您机器中的内存空间,因此它不会浪费在那里-只是您不能使用'ps'或其他类似的东西来计算您的程序使用了多少内存!)

正如评论中建议的那样:您也可以编写自己的内存分配器,使用mmap()创建一个"块"来分发部分。如果你有一段代码,做了很多的内存分配,然后所有这些肯定会被释放后,分配所有那些从一个单独的块内存,当一切都被释放,你可以把mmap地区回一个"自由mmap名单",当列表是足够大,释放一些mmap分配(这是为了避免调用mmap很多次,然后再munmap几millisconds后)。然而,如果你曾经让这些内存分配中的一个"逃逸"出你的隔离区,你的应用程序可能会崩溃(或者更糟,不是崩溃,而是使用属于应用程序其他部分的内存,并且你在某处得到一个非常奇怪的结果,例如一个用户看到了应该属于另一个用户的网络内容!)

使用valgrind查找内存泄漏:

它将列出您分配和未释放内存的位置。

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

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

当您调用freedelete时,操作系统通常不会释放内存。这些内存将返回到运行时库中的堆管理器。

如果你想真正释放内存,你可以使用brk。但是,这将引发一个非常大的内存管理问题。如果直接调用brk,最好不要调用malloc。对于c++,你可以覆盖new直接使用brk

这可不是件容易的事。

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

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

如果你在一个mspace中有一个指针指向另一个mspace中的内存,然后你删除了第二个mspace,那么正如语言律师所说的"结果是未定义的"