如何解决内存碎片

我们偶尔会遇到问题,由于内存分配失败,长期运行的服务器进程(在Windows Server 2003上运行)已经引发exception。 我们怀疑这些分配由于内存碎片而失败。

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

1)使用Windows 低碎片堆

2)jemalloc – 在Firefox 3中使用

3)Doug Lea的malloc

我们的服务器进程是使用跨平台的C ++代码开发的,所以任何解决scheme都将是理想的跨平台(do * nix操作系统遭受这种types的内存碎片?)。

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

首先,我同意那些build议资源泄露的其他海报。 你真的想先排除。

希望您正在使用的堆pipe理器有一种方法来转储堆中可用的实际可用空间总量(跨越所有空闲块)以及分配的块总数。 如果平均空闲块大小相对于堆中的总可用空间相对较小,则确实存在碎片问题。 或者,如果您可以转储最大的空闲块的大小,并将其与总可用空间进行比较,那么这将完成同样的事情。 如果您正在运行碎片,那么相对于所有块中可用的总可用空间,最大的空闲块将会很小。

要清楚地了解上述情况,在所有情况下,我们都在讨论堆中的空闲块,而不是堆中分配的块。 无论如何,如果上述条件不符合,那么你确实有某种泄漏情况。

所以,一旦你排除了泄漏,你可以考虑使用更好的分配器。 Doug Lea在这个问题中提出的malloc是一个非常好的分配器,用于一般应用程序, 大部分时间都非常强大。 换句话说,经过时间考验,大多数应用程序都能很好地工作。 然而,没有algorithm是理想的所有应用程序和任何pipe理algorithm的方法可以打破正确的pathelogical条件对其devise。

为什么你有一个分裂问题? – 碎片问题的来源是由应用程序的行为引起的,并且在同一个内存环境中与分配寿命大不相同。 也就是说,一些对象被定期分配和释放,而其他types的对象在相同的堆中持续很长一段时间。想象一下,长寿命的对象是在场地的较大区域上挖洞,从而阻止已经释放的相邻块的合并。

为了解决这类问题,你可以做的最好的事情是将堆分成逻辑上的分区,其寿命更相似。 实际上,你需要一个暂时的堆和一个持久的堆或堆,它们将类似的生命时间分组。

另一些人提出了另一种解决问题的方法,即试图使分配大小更接近或相同,但是这不太理想,因为它创build了一种称为内部碎片的不同types的碎片 – 这实际上就是浪费的空间通过在块中分配更多的内存。

另外,像Doug Lea这样的一个好的堆分配器,使得块大小更加相似是不必要的,因为分配器已经在执行两种大小的桶形scheme的function,这将完全不必要地人为地调整传递给malloc的分配大小) – 实际上,他的堆pipe理员会自动为你做更多的事情,比应用程序能够做出调整。

我想你已经错误地排除了内存泄漏。 即使是微小的内存泄漏也会导致严重的内存碎片。

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

这看起来像是一个非常小的泄漏, 当监视总分配的内存大小时您几乎不会注意到它 。 但是这个泄漏最终会导致你的应用程序内存看起来像这样:


免费 – 10MB


[分配-1字节]


免费 – 10MB


[分配-1字节]


免费 – 10MB

这个泄漏不会被注意到,直到你想分配11MB
假设您的小型转储器包含完整的内存信息,我build议使用DebugDiag来查找可能的泄漏。 在生成的内存报告中, 仔细检查分配计数(不是大小)

正如你所build议的,Doug Lea的malloc可能工作的很好。 这是跨平台,它已被用于运输代码。 至less,应该很容易集成到您的代码进行testing。

在固定的内存环境中工作了很多年,这种情况当然是一个问题,即使在非固定的环境中也是如此。 我们发现CRT分配器在性能(速度,浪费的空间效率等)方面往往很糟糕。 我坚信,如果你长期需要一个很好的内存分配器,你应该自己写(或者看看dlmalloc是否可以工作)。 诀窍是得到一些能够处理分配模式的东西,而这与内存pipe理效率有关,就像其他任何东西一样。

给dlmalloc一个尝试。 我绝对赞成。 它也是可调的,所以你可以通过改变一些编译时间选项来获得更高的效率。

老实说,你不应该依赖于新的操作系统实现“离开”的东西。 N年后的服务包,补丁或其他新操作系统可能会使问题变得更糟。 同样,对于需要强大内存pipe理器的应用程序,请不要使用编译器提供的库存版本。 find一个适合你的情况。 从dlmalloc开始,调整它,看看你是否能得到最适合你的情况的行为。

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

例如,对于运行服务器端脚本的Web服务器,可能会创build一个string来输出页面。 而不是为每个页面请求分配和释放这些string,只是维护一个池,所以你只需要分配,当你需要更多,但不是释放(意味着一段时间后,你得到的情况,你不再分配,因为你有足够)

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

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

对于内存密集的数据结构,您可以切换到可重用的存储池机制。 你可能也可以在栈上分配更多的东西,而不是堆,但实际上,我认为这并不会产生太大的影响。

我会启动一个像valgrind这样的工具,或者做一些密集的日志logging来查找没有被释放的资源。

@nsaners – 我很确定问题是内存碎片。 我们已经分析了在分配大量(5-10mb)内存时minidump指向一个问题。 我们还监视了进程(现场和开发中)以检查内存泄漏 – 没有检测到(内存占用通常很低)。

这个问题确实发生在Unix上,尽pipe通常不会那么糟糕。

Low-framgmentation堆帮助了我们,但是我的同事发誓Smart Heap (它已经在我们的几个产品中使用了多年的跨平台)。 不幸的是,由于其他情况,这次我们无法使用Smart Heap。

我们也看看块/块分配,并试图让范围精明的池/策略,即在这里长期的事情,整个请求的东西,那里的短期事情,等等。

像往常一样,你通常可以浪费记忆来获得一些速度。

这个技术对于一个通用的分配器没有用,但是它确实有用。

基本上,这个想法是编写一个分配器,从池中返回所有分配大小相同的内存。 这个游泳池永远不会变得碎片化,因为任何一个游戏块都和另一个一样好 您可以通过创build具有不同大小块的多个池来减less内存浪费,并select仍然大于请求量的最小块大小池。 我用这个想法来创build在O(1)中运行的分配器。

如果你在谈论Win32的话 – 你可以尝试使用LARGEADDRESSAWARE来挤压一些东西。 您将拥有〜1Gb额外的碎片整理内存,以便您的应用程序将其分段更长时间。