如何处理外部碎片,分页如何帮助解决外部碎片问题?

时间:2017-07-05 19:40:59

标签: memory-management paging dynamic-memory-allocation fragmentation memory-fragmentation

我知道关于我在这里指出的问题有很多问题,但我找不到任何复杂的答案(在StackOverflow和其他来源都没有。)

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

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

这很清楚。当“分页”出现时,问题就开始了。

有时我可以找到分页解决外部碎片问题的信息。 事实上,我同意,由于分页,操作系统能够创建内存的几乎连续的区域,分配给进程,即使物理上内存的各个部分都是分散的。

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

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

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

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

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

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

我已经把问题加粗了,所以匆忙的人可以在不阅读所有内容的情况下找到问题。

问候!

1 个答案:

答案 0 :(得分:2)

"分段"确实不是一个非常精确的术语。但是我们可以肯定地说,当正在运行的应用程序需要一个n字节块并且有n个或更多字节未被使用时,我们无法获得所需的块,然后& #34;内存过于分散。"

  

但是[分页]究竟是如何帮助进行外部分配[我认为你的意思是碎片]?

这里真的没什么复杂的。 外部碎片是分配的块之间的空闲内存,这些块太小了#34;满足任何申请要求。这是一般概念。 "太小"的定义是依赖于应用程序的。尽管如此,如果分配的块可以落在任何边界上,那么在许多分配和解除分配之后很容易发生大量此类片段。分页以两种方式帮助处理外部碎片。

  • 首先,它将内存细分为固定大小的相邻块 - 页面 - 足够大"所以他们从来没有用过。再次定义"足够大"不精确。但是大多数应用程序将有一个4k页面可以满足的许多要求。由于页面分配不会出现外部碎片问题,因此问题已得到缓解。

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

  

但是在分配较小的情况下呢?

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

  

所以问题是如何处理外部分配[我假设你的意思是碎片]?那么(假设最坏的情况)迟早,无论是分页还是不分页,分配和释放堆内存(不同大小)的长工作应用程序都会因为外部碎片而陷入低内存状态?

如果我理解正确,你就会问是否碎片是不可避免的。除非在非常特殊的条件下(例如,应用程序只需要一个大小的块),答案是肯定的。但这并不意味着它一定是个问题。

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

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

还有一点需要注意:使用copying/compacting garbage collection完全消除碎片是可能的。当然,这需要基础语言支持,并且需要支付绩效账单。复制垃圾收集器通过移动对象来压缩堆,以便在它运行以回收存储时完全消除未使用的空间。为此,它必须将正在运行的程序中的每个指针更新到相应对象的新位置。虽然这可能听起来很复杂,但我实施了一个复制垃圾收集器,并没有那么糟糕。算法非常酷。不幸的是,许多语言(例如C和C ++)的语义不允许在正在运行的程序中找到每个指针,这是必需的。

  

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

虽然通用分配器很好,但它们并不能保证。安全关键或硬实时约束系统完全避免堆使用并不罕见。另一方面,当不需要绝对保证时,通用分配器通常很好。有许多系统使用通用分配器可以在较长时间内与恶劣负载完美运行:碎片达到可接受的稳定状态并且不会导致问题。

  

还有一个问题..内部碎片是一个含糊不清的术语吗?

这个词并不含糊,但在不同的语境中使用。不变量是它指的是分配块内未使用的内存。

操作系统文献倾向于假设分配单位是页面。例如,Linux sbrk允许您请求在任何地方设置数据段的末尾,但Linux分配页面而不是字节,因此从操作系统的角度来看,最后一页的未使用部分是内部碎片

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