是否有可能完全避免堆碎片?

时间:2014-02-21 22:59:05

标签: c++ memory-management

例如,如果动态内存的释放总是在与分配相反的方向上完成。在这种情况下,是否可以保证堆不会碎片?

从理论的角度来看:对于一个非常重要的应用来管理内存以完全避免堆碎片,是否存在一些现实的方法? (在堆中的每次原子更改之后,堆仍然是未分段的吗?)

7 个答案:

答案 0 :(得分:19)

  

对于非常重要的应用程序存在一些现实的方法如何管理内存以完全避免堆碎片

是。分配堆栈中的所有内容。这似乎是一种完美的建议,但我已经在非平凡的计划中完成了它。

编辑为了绝对清晰并避免怀疑,我说' 堆栈'。我明确指的是自动存储,即本地变量堆栈,而不是任何堆栈类。关于此的一些评论基本上是迟钝的。

答案 1 :(得分:5)

真正避免堆碎片的唯一方法是自己进行内存管理,跟踪所使用的所有指针,并不时对堆进行碎片整理。但请注意,有一个原因可以解决这个问题:它没有得到回报。您需要花费在对堆进行碎片整理的代码和性能上的努力太大了。

只是一段轶事历史信息:
旧的MacOS(MacOS-X之前的版本!)使用所谓的内存对象句柄:指向实际内存区域的指针列表。这样做的好处是操作系统可以通过修改表中的指针来移动内存对象;句柄的任何引用都将保持不变。但是,每次要在系统调用中访问其数据时,都必须锁定句柄。应该提到的是,这是在多核成为主流之前,因此没有真正的并发性发生。使用多线程应用程序时,每次都必须锁定句柄,至少如果允许其他线程调用系统。我再说一遍,有一个原因是句柄在MacOS-X中无法存活......

答案 2 :(得分:3)

我认为理论上可以实现这一点(假设堆实现也在做“正确的事情”[例如,在释放时立即合并块])

但在解决某些实际问题的任何实际应用中,这都不太可能。当然,std::stringstd::vector的任何使用几乎肯定会以“无序方式”分配/释放内存。

如果你有一个堆碎片是一个潜在问题的场景,那么使用一个减少/消除这个问题的解决方案几乎肯定会更好(例如固定大小的桶分配,各种类型的分配的单独堆 - 这些只是MANY中的两个不同的解决方案)

答案 3 :(得分:3)

可以将堆分区为允许特定大小的内存分配的区域。这样你的堆就不会被分区了,但是你会有内存消耗开销和没有免费块的风险,但有时它是合理的(比如软件必须24/7/365保持在线状态。大小可以是权力2,或最常用的应用程序大小,或者只是在第一次分配大小N到一些合理N时按需分配的新内存区域。

它看起来与此类似(* - 已分配的块, - 空闲块

 size=1: [*][ ][*][*][*][ ][ ][ ][ ][ ]
 size=2: [  ][  ][  ][**][**][  ][  ][  ]
 size=4: [    ][****][    ][    ][    ]
 size=8: [        ][        ][        ]
 ....

我刚刚在实验过主题时写过的可视化工具:http://bobah.net/d4d/tools/cpp-heapmap

答案 4 :(得分:2)

在Java等托管语言中,堆碎片整理一直在发生。这是可能的,因为Java“指针”实际上是引用,Java知道它们,并且能够在它移动堆中的对象时移动它们。

在C和C ++中,指针可以是任何东西,它们可以被计算,组合,更新等。这意味着一旦分配了某些东西就无法移动,因为你永远不知道它实际上是什么指针 - 而且在一般情况下,碎片整理是不可能的。

减少碎片的唯一方法是只在相同的内存区域中存储相同大小的元素,但这是如此不灵活,对于许多现实生活中的编程情况来说是不切实际的。

答案 5 :(得分:2)

如上面的delnan所述,另一个问题是当存在大量分配和释放内存时可能发生的虚拟内存页碎片。 Windows .net应用程序依赖于.net的垃圾收集方案来重新打包已分配的内存,但这会导致正在运行的程序暂停。我不知道每次停顿多久。

答案 6 :(得分:1)

我在硬实时系统中做了很多软件设计和编程。这些是非常关键的实时系统,可控制炼油厂,发电厂,钢铁厂,铜冶炼厂,石油化工厂,石油和天然气管道的储存和运输设施。我们不能允许任何会导致功能丧失或重启任何控制服务器的内存碎片,因为结果将是最小的经济损失,最坏的是灾难性的损失和生命损失。

我们只创建了三个固定的缓冲区大小:小型,中型和大型,在启动时我们将所有内存预分配到3个确切的大小,我们通过一个简单的链表自己管理内存。不需要垃圾收集,但我们当然必须明确分配和释放缓冲区。