如何处理外部碎片,分页如何帮助外部碎片?

How to deal with external fragmentation, how paging helps with external fragmentation?

我知道有很多关于我在这里指出的问题的问题,但我找不到任何复杂的答案(无论是在 Whosebug 上还是在其他来源中)。

我想问一下堆(RAM)碎片问题。

据我了解,有两种碎片: internal - 与分配单元大小(AU)和分配内存AM大小的差异有关(浪费内存等于AM % AU) , external - 与空闲内存的不连续区域相关,因此即使空闲内存区域的总和可以处理新的分配请求,它也会失败如果没有继续区域可以处理它。

这很清楚。当 "paging" 出现时,问题就开始了。

有时我能找到分页解决外部碎片问题的信息。 事实上,我同意,由于分页,OS 能够创建内存的虚拟连续区域,分配给进程,即使内存的物理部分是分散的。

但它究竟对外部碎片有何帮助? 我的意思是,假设页面大小为 4kB,而我们要分配 16kB,那么当然我们只需要找到四个空页面框架,即使这些框架在物理上不是连续区域的一部分。

但是如果分配较小呢? 我相信页面本身仍然可能是碎片化的,并且(在最坏的情况下)如果旧框架不能用于分配请求的内存,OS 仍然需要提供一个新框架。

那么(假设最坏的情况)迟早,无论是否有分页,分配和释放堆内存(不同大小)的长时间工作的应用程序都会陷入低内存状态,因为外部碎片化?

那么问题是如何处理外部碎片? 自己实现分配算法?分页(如我所写,不确定是否有帮助)?还有什么 ? OS (Windows, Linux) 是否提供一些碎片整理方法?

最根本的解决方案是禁止使用堆,但是对于具有分页、虚拟地址空间、虚拟内存等的平台来说真的有必要吗...唯一的问题是应用程序需要 运行 不可阻挡多年?

还有一个问题.. 内部碎片是一个模棱两可的术语吗? 我在某处发现了内部碎片指向页面框架部分的定义,这是浪费,因为进程不需要更多内存,但单个框架不能由多个进程拥有。

我把问题加粗了,所以赶时间的人不用看全文也能找到问题。

此致!

"Fragmentation"确实不是一个很精确的词。但我们可以肯定地说,当一个 运行ning 应用程序需要一个 n 字节的块并且有 n 或更多字节未使用时,我们无法获得所需的块, 然后 "memory is too fragmented."

But how exactly does it [paging] help with the external allocation [I assume you mean fragmentation] ?

这里真的没有什么复杂的。 外部碎片 是已分配块之间的可用内存,"too small" 可满足任何应用程序要求。这是一个普遍的概念。 "too small" 的定义取决于应用程序。尽管如此,如果分配的块可以落在任何边界上,那么在多次分配和释放之后,很容易出现许多这样的碎片。分页以两种方式帮助处理外部碎片。

  • 首先,它将内存细分为固定大小的相邻块 - 页 - "large enough" 因此它们永远不会无用。同样,"large enough" 的定义并不准确。但是大多数应用程序将有很多需求可以通过一个 4k 页面来满足。由于分配一个页面或更少的页面不会发生外部碎片问题,因此该问题已得到缓解。

  • 其次,分页硬件提供了应用程序页面和物理内存页面之间的间接级别。因此,任何空闲的物理内存页面都可以用来帮助满足任何应用程序请求,无论请求有多大。例如,假设您有 100 个物理页面,每隔一个物理页面(其中 50 个)分配一次。在没有页面映射硬件的情况下,可以满足的最大连续内存请求是 1 页。加上映射,它有 50 页。 (我忽略了最初分配的没有映​​射物理页面的虚拟页面。那是另一个讨论。)

But what in case of the smaller allocation ?

还是很简单的。如果分配单位是页,则任何小于页的分配都会产生未使用的部分。这是内部碎片:已分配块中的内存不可用。您制作的分配单元越大(它们不必是单个页面),由于内部碎片而无法使用的内存就越多。平均而言,这将趋向于一个分配单元的一半。因此,尽管 OS 倾向于以页面为单位进行分配,但大多数应用程序端内存分配器从 OS 请求非常少量(通常是一个)的大块(页面)。他们在内部使用更小的分配单元:4-16 字节很常见。

So the question is how to deal with the external allocation [I assume you mean fragmentation] ? So is it that (assuming the worst case) sooner or later, with paging or without, the long working application that allocates and releases the heap memory (different sizes) will fall into low-memory condition, because of external fragmentation ?

如果我没理解错的话,你是在问碎片化是否不可避免。除非在非常特殊的情况下(例如应用程序只需要一种尺寸的块),答案是肯定的。但这并不意味着它一定是个问题。

内存分配器使用可以非常有效地限制碎片的智能算法。例如,他们可以维护 "pools" 不同的块大小,使用块大小与给定请求最匹配的池。这往往会限制内部和外部碎片。 dlmalloc 是一个有据可查的真实示例。源码也很清楚。

当然,任何通用分配器都可能在特定条件下失败。出于这个原因,现代语言(我知道 C++ 和 Ada 是两种语言)允许您为给定类型的对象提供专用分配器。通常 - 对于固定大小的对象 - 这些可能只是维护一个预涂涂层的空闲列表,因此该特定情况下的碎片为零,并且 allocation/deallocation 非常快。

请注意:使用 copying/compacting garbage collection 可以完全消除碎片。当然,这需要底层语言支持,并且需要支付性能费用。复制垃圾收集器通过移动对象来压缩堆,以在 运行 需要回收存储时完全消除未使用的 space 。为此,它必须将 运行ning 程序中的每个指针更新为相应对象的新位置。虽然这听起来可能很复杂,但我已经实现了一个复制垃圾收集器,而且还不错。这些算法非常酷。不幸的是,许多语言(例如 C 和 C++)的语义不允许在 运行ning 程序中找到每个指针,这是必需的。

The most radical solution is to forbid using of the heap, but is it really necessary for the platforms with paging, virtual address spaces, virtual memory etc ... and the only issue is that the applications need to run unstoppable for a years ?

虽然通用分配器很好,但不能保证一定会用到。安全关键或硬实时受限系统完全避免使用堆并不罕见。另一方面,当不需要绝对保证时,通用分配器通常就可以了。有许多系统 运行 使用通用分配器可以在长时间内完美地应对艰难的负载:碎片达到可接受的稳定状态并且不会引起问题。

One more issue.. is internal fragmentation an ambiguous term ?

这个词没有歧义,但在不同的上下文中使用。不变的是它指的是分配块内未使用的内存。

OS 文献倾向于假设分配单元是页面。例如,Linux sbrk 允许您请求将数据段的末尾设置在任何位置,但是 Linux 分配页面而不是字节,因此最后一页未使用的部分是来自 [=71= 的内部碎片]的观点。

面向应用程序的讨论倾向于假设分配在 "blocks" 或 "chunks" 中任意大小。 dlmalloc 使用大约 128 个离散块大小,每个都在自己的空闲列表中维护。 Plus,它将使用 OS 内存映射系统调用自定义分配非常大的块,因此在请求和实际分配之间最多有一个页面大小(减去 1 字节)不匹配。显然,要最大限度地减少内部碎片会带来 很多 的麻烦。给定分配导致的碎片是请求与实际分配的块之间的差异。由于块大小如此之多,因此这种差异受到严格限制。另一方面,许多块大小增加了外部碎片问题的可能性:可用内存可能完全由 dlmalloc 管理良好的块组成,但太小而无法满足应用程序要求。