关于c ++:如何解决内存碎片

关于c ++:如何解决内存碎片

How to solve Memory Fragmentation

由于内存分配失败,长时间运行的服务器进程(在Windows Server 2003上运行)会引发异常,因此有时会遇到问题。 我们怀疑这些分配由于内存碎片而失败。

因此,我们一直在寻找一些可能对我们有帮助的替代内存分配机制,我希望有人可以告诉我最好的一种:

1)使用Windows低碎片整理堆

2)jemalloc-在Firefox 3中使用

3)道格·李的malloc

我们的服务器进程是使用跨平台C ++代码开发的,因此任何解决方案在理想情况下也将是跨平台的(* nix操作系统会遭受这种类型的内存碎片吗?)。

另外,我是否认为LFH现在是Windows Server 2008 / Vista的默认内存分配机制?...如果我们的客户仅升级服务器操作系统,我当前的问题会"消失"吗?


首先,我同意其他暗示资源泄漏的海报。您真的想先排除掉它。

希望您当前正在使用的堆管理器可以转储堆中可用的实际总可用空间(跨所有可用块)以及分配给它的总块数。如果平均可用块大小与堆中的总可用空间相比较小,那么您确实会遇到碎片问题。或者,如果您可以转储最大可用块的大小并将其与总可用空间进行比较,则将完成相同的操作。如果您要碎片化,则最大的可用块相对于所有块上可用的总可用空间将很小。

为了清楚地了解上述内容,在所有情况下,我们都在谈论堆中的可用块,而不是堆中已分配的块。无论如何,如果不满足上述条件,则确实存在某种泄漏情况。

因此,一旦排除了泄漏,就可以考虑使用更好的分配器。问题中建议的道格·利阿(Doug Lea)的malloc是一个非常好的通用程序分配器,并且在大多数情况下都非常可靠。换句话说,它已经过时间测试,可以在大多数应用程序中很好地工作。但是,没有一种算法适合所有应用程序,并且任何管理算法方法都可能因其设计的正确病理条件而被打破。

为什么会有碎片问题? -碎片问题的根源是由应用程序的行为引起的,并且与同一内存领域中的分配生存期有很大不同。也就是说,某些对象被定期分配和释放,而其他类型的对象则在同一堆内存中持续较长的时间.....考虑使用更长寿命的对象,例如在舞台上较大的区域戳孔,从而防止合并已释放的相邻块。

为了解决这种类型的问题,您可以做的最好的事情是从逻辑上将堆划分为生命周期更相似的子区域。实际上,您需要一个临时堆和一个或多个永久堆,这些堆将具有相似生存期的事物进行分组。

其他一些人提出了另一种方法来解决该问题,即尝试使分配大小更相似或更相同,但这并不理想,因为它会创建另一种类型的碎片,称为内部碎片-实际上,这是浪费的空间通过在块中分配比您需要更多的内存。

此外,对于像Doug Lea一样的优秀堆分配器,不需要使块大小更相似,因为分配器已经在执行两种大小的存储桶方案,这将使得完全无需人为地调整传递给malloc( )-实际上,他的堆管理器会自动比应用程序能够进行调整的功能更强大。


我认为您错误地排除了内存泄漏为时过早。
即使很小的内存泄漏也可能导致严重的内存碎片。

假设您的应用程序的行为如下:
分配10MB
分配1个字节
免费10MB
(糟糕,我们没有释放1个字节,但谁在乎1个小字节)

这似乎是一个很小的泄漏,仅监视分配的总内存大小时几乎不会注意到它。
但是,此泄漏最终将导致您的应用程序内存如下所示:


免费– 10MB


[分配-1字节]


免费– 10MB


[分配-1字节]


免费– 10MB

在您要分配11MB之前,不会注意到此泄漏。
假设您的小型转储包含完整的内存信息,我建议使用DebugDiag找出可能的泄漏。
在生成的内存报告中,仔细检查分配计数(而不是大小)。


如您所建议,Doug Lea的malloc可能工作良好。它是跨平台的,已在运输代码中使用。至少,应该很容易将其集成到代码中进行测试。

在固定内存环境中工作了多年之后,即使在非固定环境中,这种情况也肯定是一个问题。我们已经发现,CRT分配器在性能(速度,浪费空间的效率等)方面趋于恶臭。我坚信,如果您长期以来一直需要良好的内存分配器,则应该编写自己的内存分配器(或查看dlmalloc之类的东西是否可以工作)。诀窍是编写一些与您的分配模式配合使用的东西,并且与内存管理效率几乎和其他任何东西都息息相关。

尝试dlmalloc。我肯定会竖起大拇指。它也是相当可调的,因此您可以通过更改一些编译时选项来提高效率。

老实说,您不应该依赖于新的OS实施"消失"的事情。 N年后发布的Service Pack,补丁程序或其他新OS可能会使问题变得更糟。同样,对于需要强大的内存管理器的应用程序,请勿使用编译器可用的常规版本。寻找一种适合您的情况。从dlmalloc开始并对其进行调整,以查看是否可以获得最适合您的情况的行为。


您可以通过减少分配的解除分配的数量来帮助减少碎片。

例如例如,对于运行服务器端脚本的Web服务器,它可能会创建一个字符串以将页面输出到该字符串。只需为它们分配一个池,而不是为每个页面请求分配和取消分配这些字符串,所以只有在需要更多时才分配它们,但是您不会取消分配(意味着一段时间后,您也不再分配了,因为您有足够)

您可以使用_CrtDumpMemoryLeaks();在运行调试版本时将内存泄漏转储到调试窗口,但是我相信这特定于Visual C编译器。 (在crtdbg.h中)


像往常一样,通常可以浪费内存来提高速度。

该技术对通用分配器没有用,但确实有它的位置。

基本上,这个想法是编写一个分配器,该分配器从所有分配大小相同的池中返回内存。该池永远不会成为碎片,因为任何块都与另一个块一样好。您可以通过创建具有不同大小块的多个池并选择仍大于请求数量的最小块大小池来减少内存浪费。我已经使用这种想法来创建在O(1)中运行的分配器。


该问题确实在Unix上发生,尽管通常情况没有那么糟。

低碎片堆对我们有所帮助,但我的同事对Smart Heap发誓
(多年来,它已在我们的几个产品中跨平台使用)。不幸的是,由于其他情况,我们这次无法使??用Smart Heap。

我们还将研究块/块分配,并尝试拥有精通范围的池/策略,即
长期的东西,整个请求在那里,短期的东西,等等。


@nsaners-我很确定问题出在内存碎片上。我们已经分析了小型转储,它指出了在分配大块内存(5-10mb)时出现的问题。我们还监视了过程(现场和开发中)以检查内存泄漏-未检测到泄漏(内存占用量通常很低)。


在怀疑碎片之前,我会先怀疑泄漏。

对于占用大量内存的数据结构,您可以切换到可重复使用的存储池机制。您也许还可以在堆栈上分配更多的东西,而不是在堆上分配东西,但实际上,这不会产生太大的变化。

我将启动valgrind之类的工具,或进行一些密集的日志记录以查找未释放的资源。


一种简单,快速且肮脏的解决方案是将应用程序分为多个进程,每次创建进程时都应获得新鲜的HEAP。

您的内存和速度可能会有所下降(交换),但是快速的硬件和较大的RAM应该可以提供帮助。

当线程尚不存在时,这是带有守护程序的UNIX旧技巧。


如果您谈论Win32,则可以尝试使用LARGEADDRESSAWARE进行压缩。您将拥有约1Gb的额外碎片整理内存,因此您的应用程序会将其碎片化的时间更长。


推荐阅读