如何解决内存碎片

时间:2020-03-05 18:52:50  来源:igfitidea点击:

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

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

1)使用Windows低碎片整理堆

2)在Firefox 3中使用的jemalloc

3)道格·李的malloc

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

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

解决方案

回答

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

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

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

回答

我们可以通过减少分配的分配量来帮助减少碎片。

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

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

回答

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

回答

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

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

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

回答

像往常一样,通常可以浪费内存以获得一定速度。

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

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

回答

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

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

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

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

为什么会有碎片问题?碎片问题的根源是由应用程序的行为引起的,并且与同一内存领域中的分配生存期有很大不同。也就是说,某些对象被定期分配和释放,而其他类型的对象则在同一堆内存中保留了较长的时间。合并已释放的相邻块。

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

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

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

回答

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

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

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

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

回答

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

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

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


免费10MB


[分配-1字节]


免费10MB


[分配-1字节]


免费10MB

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