如何解决内存碎片问题

c++ windows memory

41871 观看

10回复

10591 作者的声誉

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

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

1)使用Windows 低碎片堆

2)jemalloc - 用于Firefox 3

3)Doug Lea的malloc

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

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

作者: Alan 的来源 发布者: 2008 年 9 月 13 日

回应 10


1

8580 作者的声誉

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

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

我会启动像valgrind这样的工具,或者进行一些密集的日志记录来查找未发布的资源。

作者: nsanders 发布者: 2008 年 9 月 13 日

2

15326 作者的声誉

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

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

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

作者: Fire Lancer 发布者: 2008 年 9 月 13 日

1

10591 作者的声誉

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

作者: Alan 发布者: 2008 年 9 月 13 日

1

2383 作者的声誉

问题确实发生在Unix上,虽然它通常没那么糟糕。

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

我们还看看块/块分配并尝试拥有精通范围的池/策略,即这里的长期事物,那里的整体请求,那里的短期事物等。

作者: maccullt 发布者: 2008 年 9 月 13 日

1

21031 作者的声誉

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

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

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

作者: dicroce 发布者: 2008 年 9 月 13 日

34

8057 作者的声誉

决定

首先,我同意其他提出资源泄漏的海报。你真的想先排除它。

希望您当前使用的堆管理器有一种方法可以转储堆中可用的实际总可用空间(跨所有空闲块)以及它被分割的块总数。如果平均空闲块大小与堆中的总可用空间相比相对较小,那么您确实存在碎片问题。或者,如果您可以转储最大空闲块的大小并将其与总可用空间进行比较,那么这将完成相同的任务。如果您遇到碎片,则相对于所有块中可用的总可用空间,最大的空闲块将很小。

为了清楚上述内容,在所有情况下,我们都在讨论堆中的空闲块,而不是堆中分配的块。在任何情况下,如果不满足上述条件,那么您确实存在某种泄漏情况。

因此,一旦排除了泄漏,您可以考虑使用更好的分配器。Doug Lea的malloc在问题中提出了一个非常好的通用应用程序分配器,并且在大多数情况下非常强大。换句话说,它经过时间测试,可以很好地适用于大多数应用程序。但是,没有算法适用于所有应用程序,并且任何管理算法方法都可以通过针对其设计的正确的语法条件来打破。

为什么你有碎片问题?- 碎片问题的来源是由应用程序的行为引起的,并且与同一内存领域中的大大不同的分配生存期有关。也就是说,一些对象被定期分配和释放,而其他类型的对象在同一堆中持续很长一段时间......想想更长寿命的对象是在竞技场的更大区域戳洞,从而防止已释放的相邻块的合并。

为了解决这类问题,你可以做的最好的事情是将堆逻辑地划分为生命周期更相似的子竞技场。实际上,您需要一个临时堆和一个持久堆或堆,它们将相似生命周期的事物分组。

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

另外,使用像Doug Lea这样的好的堆分配器,使得块大小更相似是不必要的,因为分配器已经具有两个大小的分段方案的功能,这将使得完全没有必要人为地调整传递给malloc的分配大小( ) - 实际上,他的堆管理器会自动为您执行此操作,而不是应用程序可以进行调整。

作者: Tall Jeff 发布者: 2008 年 9 月 14 日

5

8594 作者的声誉

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

在固定内存环境中工作了很多年,这种情况肯定是一个问题,即使在非固定环境中也是如此。我们发现CRT分配器在性能(速度,浪费空间效率等)方面往往很糟糕。我坚信如果你在很长一段时间内都需要一个好的内存分配器,你应该自己编写(或者看看像dlmalloc这样的东西是否可行)。诀窍是获得一些适用于您的分配模式的内容,这与内存管理效率几乎完全不同。

试试dlmalloc吧。我绝对赞不绝口。它也是相当可调的,因此您可以通过更改一些编译时选项来提高效率。

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

作者: Mark 发布者: 2008 年 9 月 14 日

15

1358 作者的声誉

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

假设您的应用程序行为如下:
分配10MB
分配1个字节
空闲10MB
(oops,我们没有释放1个字节,但谁关心1个小字节)

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


免费 - 10MB


[分配-1字节]


免费 - 10MB


[分配-1字节]


免费 - 10MB


这个泄漏将不会被注意到...直到你想要分配11MB
假设你的minidumps包含完整的内存信息,我建议使用DebugDiag来发现可能的泄漏。在生成的内存报告中,仔细检查分配计数(不是大小)

作者: Tal 发布者: 2008 年 10 月 12 日

-1

391 作者的声誉

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

作者: Serov Danil 发布者: 2016 年 11 月 29 日

0

11 作者的声誉

简单,快速和肮脏的解决方案是将应用程序拆分为多个流程,每次创建流程时都应该获得新的HEAP。

您的内存和速度可能会受到一些影响(交换),但快速的硬件和大RAM应该能够提供帮助。

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

作者: kris2k 发布者: 2018 年 11 月 3 日
32x32