2011-10-22 88 views
13

假设我有一个构造函数的指针一大块内存PTR和大小N.如果我做很多随机的分配和各种尺寸的解除分配的内存池对象,我可以在这样的状态下得到的内存,我不能在内存中连续分配一个M字节的对象,即使可能有很多空闲!与此同时,我无法压缩内存,因为这会在消费者身上造成悬挂指针。在这种情况下,如何解决碎片问题?处理内存池中的碎片?

+0

你是想实现一个操作系统或至少其中的一部分?内存池比正常分配更受欢迎的唯一原因是正常分配涉及碎片。 – Dani

回答

8

我想添加我的2美分只是因为没有人指出你的描述听起来像你正在实施一个标准的堆分配器(我。e当我们调用malloc()或operator new时,我们所有人都已经使用过)。

堆正是这样一个对象,即进入虚拟内存管理器和大块的内存要求(你所说的“一池”)。然后它有各种不同的算法来处理分配各种大小块并释放它们的最有效方式。此外,很多人多年来一直在修改和优化这些算法。长期以来,Windows带有一个称为低碎片堆(LFH)的选项,您必须手动启用该选项。从Vista开始LFH默认情况下用于所有堆。

堆并不完美,如果没有适当的使用肯定可以拖垮性能。由于操作系统供应商无法预测您将使用堆的每种情况,因此他们的堆管理员必须针对“平均”使用进行优化。但是,如果你有一个条件类似于用于常规堆的要求(即多个对象,不同大小的....),你应该考虑只使用一个堆,而不是彻底改造它,因为机会是你的实现将不如什么操作系统已经为你提供了。

内存分配,可以通过简单地不使用堆提升性能的唯一时间是通过放弃一些其他方面(分配开销,分配一辈子....),这是不是您的具体应用非常重要。

例如,在我们的应用中有要求的少很多分配比1KB但这些拨款只被用于时间(毫秒)非常短的时间。为了优化应用程序,我用升压池库,但让我的“分配器”实际上包含升压池对象的集合,每个负责从16个字节分配一个特定尺寸可达1024(在4个步骤)扩展它。这提供了几乎免费的(O(1)复杂性)分配/释放这些对象,但美中不足的是,一个),内存使用率始终是大不降,即使我们没有被分配一个单独的对象,B)升压游泳池从未释放它所使用的内存(至少在我们使用它的模式下),所以我们只使用它来保存不会很长时间的对象。正常的内存分配的

所以这(些)方面,你是否愿意在你的应用程序要放弃?

+1

非常好的解释谢谢。 – user805547

6

根据不同的系统有几个方法可以做到这一点。

尽量避免在第一时间碎片化,如果以2的幂分配块,你也少了导致这种碎裂的机会。还有其他一些方法,但是如果你达到这个状态,那么你在这个时候只是OOM,因为除了杀死请求内存的进程之外,没有任何处理它的微妙方法,直到你可以分配内存,或者返回NULL作为你的分配区域。

另一种方式是指针传递给您的数据的指针(例如:INT **)。然后,您可以重新安排程序下面的内存(我希望线程安全)并压缩分配,以便您可以分配新块并仍旧保留旧块中的数据(一旦系统进入此状态,虽然这会变成沉重的开销,但应该很少做完了)。

也有“分箱”内存的方法,以便您有连续的页面,例如专用1页仅用于分配512和更少,另一个用于1024和更少等等......这使得更容易做出有关要使用哪个bin,在最坏的情况下,您从下一个最高的bin中拆分,或者从较低的bin合并,这样可以减少跨多个页面的碎片化概率。

0
  • 写入池作为分配列表进行操作,然后可以根据需要进行扩展和销毁。这可以减少碎片。
  • 和/或实施分配转移(或移动)的支持,所以你可以压缩主动分配。该对象/持有者可能需要协助您,因为该池可能不一定知道如何传输类型本身。如果池与集合类型一起使用,那么完成压缩/传输就容易得多。
3

对于您经常分配的对象实施object pools将显着降低分段速度,而无需更改内存分配程序。

1

这将有助于更准确地知道你实际上想要做什么,因为有很多方法可以解决这个问题。
但是,第一个问题是:这是真的发生了吗?还是理论上的问题?

有一点要记住的是,你通常有更多的虚拟内存地址空间比物理内存可用,所以即使当物理内存碎片,还有大量的连续虚拟内存。 (当然,下面的物理内存是不连续的,但是你的代码看不到这些)。

我认为有时候会有内存碎片的不必要的担心,因此人们编写一个自定义的内存分配器(或者更糟的是,他们用手柄和可移动的内存和压缩来构造一个方案)。我认为这些在实践中很少需要,它有时可以提高性能,将其抛出并返回使用malloc。