8

我需要对内存池的概念和实现进行一些说明。

通过wiki上的内存池,它说

也称为固定大小块分配,...,因为这些实现由于块大小可变而遭受碎片,由于性能原因,不可能在实时系统中使用它们。

“可变块大小导致碎片”如何发生?固定大小的分配如何解决这个问题?这个 wiki 描述对我来说听起来有点误导。我认为固定大小的分配或可变大小无法避免碎片。在内存池上下文中,通过为特定应用程序设计的特定内存分配器可以避免碎片,或者通过限制使用预期的内存块来减少碎片。

同样通过几个实现示例,例如Code Sample 1Code Sample 2,在我看来,要使用内存池,开发人员必须非常了解数据类型,然后将数据切割、拆分或组织到链接的内存中块(如果数据接近链表)或分层链接块(如果数据更分层组织,如文件)。此外,似乎开发人员必须事先预测他需要多少内存。

好吧,我可以想象这对于一组原始数据很有效。内存模型不那么明显的 C++ 非原始数据类呢?即使对于原始数据,开发人员是否应该考虑数据类型对齐?

是否有适用于 C 和 C++ 的良好内存池库?

感谢您的任何评论!

4

5 回答 5

14

可变块大小确实会导致碎片。看看我附上的图片:在此处输入图像描述

图像(来自此处)显示了 A、B 和 C 分配内存块(可变大小的块)的情况。

在某个时候,B 释放了它所有的内存块,突然间你有了碎片。例如,如果 C 需要分配一大块内存,这仍然适合可用内存,但它不能这样做,因为可用内存被分成两个块。

现在,如果您考虑每个内存块大小相同的情况,显然不会出现这种情况。

正如您自己指出的那样,内存池当然有其自身的缺点。所以你不应该认为内存池是一根神奇的魔杖。它是有成本的,在特定情况下支付它是有意义的(即内存有限的嵌入式系统、实时限制等)。

至于 C++ 中哪个内存池好,我会说这取决于。我在 VxWorks 下使用过一个操作系统提供的;从某种意义上说,一个好的内存池与操作系统紧密集成是有效的。实际上,我猜每个 RTOS 都提供了内存池的实现。

如果您正在寻找通用内存池实现,请查看.

编辑:

从您上次的评论来看,在我看来,您可能将内存池视为碎片问题的“解决方案”。不幸的是,这种情况并非如此。如果你愿意,碎片化是熵在内存层面的表现,即不可避免。另一方面,内存池是一种管理内存的方式,可以有效地减少碎片的影响(正如我所说,正如维基百科所提到的,主要是在特定系统上,如实时系统)。这是有代价的,因为内存池的效率可能低于“正常”内存分配技术,因为您有最小的块大小。换句话说,熵在伪装下重新出现。

此外,还有许多参数会影响内存池系统的效率,例如块大小、块分配策略,或者您是否只有一个内存池,或者您有多个具有不同块大小、不同生命周期或不同策略的内存池。

内存管理确实是一件复杂的事情,而内存池只是一种技术,与其他技术一样,它与其他技术相比改进了一些东西,并计算了它自己的成本。

于 2011-07-19T13:37:37.103 回答
5

在您总是分配固定大小的块的情况下,您要么有足够的空间再容纳一个块,要么没有。如果有,则该块适合可用空间,因为所有可用或已用空间的大小相同。碎片化不是问题。

在具有可变大小块的场景中,您最终可能会得到多个大小不同的独立空闲块。对大小小于可用总内存的块的请求可能无法满足,因为没有一个足够大的连续块来满足它。例如,假设您最终有两个独立的 2KB 空闲块,并且需要满足对 3KB 的请求。即使有足够的可用内存,这些块都不足以提供这一点。

于 2011-07-19T13:44:51.007 回答
2

固定大小和可变大小的内存池都具有碎片化的特点,即在使用过的内存池之间会有一些空闲内存块。

对于可变大小,这可能会导致问题,因为可能没有足够大的空闲块来满足特定请求的大小。

另一方面,对于固定大小的池,这不是问题,因为只能请求预定义大小的一部分。如果有空闲空间,则保证足够大以容纳一部分(的倍数)。

于 2011-07-19T13:42:59.150 回答
1

如果你做一个硬实时系统,你可能需要提前知道你可以在允许的最大时间内分配内存。这可以通过固定大小的内存池“解决”。

我曾经在一个军事系统上工作,我们必须计算系统可能使用的每种大小的内存块的最大可能数量。然后将这些数字加到总计中,并为系统配置了该内存量。

非常昂贵,但为防守工作。


当您有多个固定大小的池时,即使其他池中有足够的空间,您的池也可能会出现二次碎片。你如何分享它?

于 2011-07-19T13:43:56.370 回答
1

使用内存池,操作可能会像这样工作:

  1. 存储作为可用对象列表的全局变量(最初为空)。
  2. 要获取新对象,请尝试从可用的全局列表中返回一个。如果没有,则调用 operator new 在堆上分配一个新对象。分配速度非常快,这对于某些当前可能在内存分配上花费大量 CPU 时间的应用程序来说非常重要。
  3. 要释放对象,只需将其添加到可用对象的全局列表中。您可以对全局列表中允许的项目数量设置上限;如果达到上限,则对象将被释放而不是返回到列表中。该上限可防止出现大量内存泄漏。

请注意,这总是针对相同大小的单个数据类型进行的;它不适用于较大的,然后您可能需要像往常一样使用堆。

这很容易实现;我们在我们的应用程序中使用这种策略。这会在程序开始时导致一堆内存分配,但不会再发生内存释放/分配,这会导致大量开销。

于 2011-07-19T13:44:35.747 回答