处理内存池中的碎片?

Posted

技术标签:

【中文标题】处理内存池中的碎片?【英文标题】:Dealing with fragmentation in a memory pool? 【发布时间】:2011-12-13 00:51:04 【问题描述】:

假设我有一个带有构造函数的内存池对象,该构造函数接受一个指向一大块内存 ptr 和大小 N 的指针。如果我执行许多不同大小的随机分配和解除分配,我可以获得这样一种状态的内存,我无法在内存中连续分配 M 字节对象,即使可能有很多空闲空间!同时,我无法压缩内存,因为这会导致消费者指针悬空。在这种情况下如何解决碎片化?

【问题讨论】:

您是在尝试实现操作系统还是至少实现其中的一部分?内存池优于正常分配的唯一原因是正常分配处理碎片。 【参考方案1】:

我想加我的 2 美分只是因为没有人指出从你的描述中听起来你正在实现一个标准的堆分配器(即我们每次调用 malloc() 或 operator new 时都已经使用的东西)。

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

堆并不完美,如果使用不当,它们肯定会导致性能下降。由于操作系统供应商不可能预测您将使用堆的所有场景,因此必须针对“平均”使用优化他们的堆管理器。但是,如果您的要求类似于常规堆的要求(即许多对象,不同大小......),您应该考虑只使用堆而不是重新发明它,因为您的实现可能不如操作系统已经为你准备好了。

使用内存分配,您唯一可以通过放弃对您的特定应用程序不重要的其他方面(分配开销、分配生命周期......)而不是简单地使用堆来获得性能。

例如,在我们的应用程序中,我们需要许多小于 1KB 的分配,但这些分配只使用了很短的时间(毫秒)。为了优化应用程序,我使用了 Boost Pool 库,但对其进行了扩展,以便我的“分配器”实际上包含一组 boost 池对象,每个对象负责分配一个特定大小,从 16 字节到 1024 字节(以 4 为步长)。这提供了几乎免费(O(1) 复杂度)分配/释放这些对象,但问题是 a) 内存使用量总是很大并且即使我们没有分配单个对象,也永远不会下降,b) Boost Pool 永远不会释放它使用的内存(至少在我们使用它的模式下),所以我们只将它用于不会长时间停留的对象。

那么您愿意在您的应用中放弃正常内存分配的哪些方面?

【讨论】:

【参考方案2】:

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

首先尽量避免碎片化,如果您以 2 的幂次方分配块,则导致这种碎片化的机会较小。还有其他几种方法可以解决它,但是如果您达到这种状态,那么您此时就OOM,因为除了杀死请求内存的进程,阻塞直到可以分配内存或返回 NULL 作为您的分配区域。

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

还有一些“分箱”内存的方法,这样您就可以拥有连续的页面,例如,仅将 1 个页面专用于 512 及以下的分配,另一个用于 1024 及以下的分配,等等......这使得更容易决定哪个bin 使用,在最坏的情况下,您从下一个最高的 bin 拆分或从较低的 bin 合并,这减少了跨多个页面碎片的机会。

【讨论】:

【参考方案3】:

为您经常分配的对象实施object pools 将大大减少碎片,而无需更改您的内存分配器。

【讨论】:

【参考方案4】:

更准确地了解您实际尝试做什么会很有帮助,因为有很多方法可以解决这个问题。 但是,第一个问题是:这真的发生了,还是理论上的问题?

要记住的一点是,您通常拥有比物理内存更多的可用虚拟内存地址空间,因此即使物理内存碎片化,仍然有大量连续的虚拟内存。 (当然,物理内存在下面是不连续的,但您的代码看不到。)

我认为有时对内存碎片的恐惧是没有根据的,因此人们编写了一个自定义内存分配器(或者更糟糕的是,他们编造了一个带有句柄和可移动内存和压缩的方案)。我认为这些在实践中很少需要,有时将其丢弃并重新使用 malloc 可以提高性能。

【讨论】:

【参考方案5】: 将池写入操作作为分配列表,然后可以根据需要扩展和销毁。这可以减少碎片。 和/或实施分配转移(或移动)支持,以便您可以压缩活动分配。对象/持有者可能需要帮助您,因为池可能不一定知道如何传输类型本身。如果池与集合类型一起使用,那么完成压缩/传输会容易得多。

【讨论】:

以上是关于处理内存池中的碎片?的主要内容,如果未能解决你的问题,请参考以下文章

进程池中的共享内存

源码解读·RT-Thread操作系统内存管理之内存池

java中 关于常量池 栈内存 堆内存

读书笔记iOS-自动释放池

Mysql Innodb体系结构

线程池