【发布时间】:2012-01-14 03:48:19
【问题描述】:
我正在阅读 Andrew Tanenbaum 的 Modern Operating Systems,他写道,最适合的是一种广泛使用的内存分配算法。 他还写道,它比第一次拟合/下一次拟合要慢,因为它必须搜索整个分配内存列表。而且它往往会浪费更多的内存,因为它会在内存中留下许多无用的小间隙。
为什么它被广泛使用?这是我忽略的一些明显优势吗?
【问题讨论】:
我正在阅读 Andrew Tanenbaum 的 Modern Operating Systems,他写道,最适合的是一种广泛使用的内存分配算法。 他还写道,它比第一次拟合/下一次拟合要慢,因为它必须搜索整个分配内存列表。而且它往往会浪费更多的内存,因为它会在内存中留下许多无用的小间隙。
为什么它被广泛使用?这是我忽略的一些明显优势吗?
【问题讨论】:
我认为说它浪费的内存比第一次拟合更多是一种错误的描述。与第一次拟合相比,最佳拟合最大化可用空间,尤其是在为大分配节省可用空间时。 This 博文就是一个很好的例子。
【讨论】:
首先,它不是 被广泛使用的(就像所有顺序拟合一样),也许除了在家庭作业中;)。在我看来,广泛使用的策略是分离拟合(可以非常接近最佳拟合)。
第二,最适合的策略可以通过使用各种大小的空闲列表树来实现
第三,它被认为是关于内存碎片的最佳策略之一
见
Dynamic Storage Allocation: A Survey and Critical Review
The Memory Fragmentation Problem: Solved?
有关内存管理的信息,而不是 Tannenbaum。
【讨论】:
Best fit 不是最好的分配策略,但比 first fit 和 next fit 好。原因是它遭受的碎片问题比后两者少。
考虑一个 64 字节的微堆。首先,我们按顺序分配一个 32 字节块和两个 16 字节块来填充它。然后我们释放所有块。现在堆中有三个空闲块,一个 32 字节,两个 16 字节。
使用 first fit,我们分配一个 16 字节的块。我们使用 32 字节的块(因为它在堆中的第一个!),然后将该块的剩余 16 字节拆分为一个新的空闲块。所以在堆的开头有一个 16 字节的分配块,然后是三个空闲的 16 字节块。
如果我们现在想要分配一个 32 字节的块会发生什么?我们不能!堆中还有 48 个字节空闲,但是碎片已经把我们搞砸了。
如果我们使用最佳拟合会发生什么?当我们在寻找一个空闲块用于我们的 16 字节分配时,我们会跳过堆开始处的 32 字节块,而是选择它之后的 16 字节块。这将保留 32 字节块以进行更大的分配。
我建议你把它画在纸上,这样可以很容易地看到堆在分配和释放过程中发生了什么。
【讨论】:
空间效率和多功能性确实是答案。大区块比小区块更能满足未知的未来需求,因此最佳拟合算法会首先尝试使用最小的区块。
First-fit 和 next-fit 算法(也可以分割块)最终可能首先使用较大块的片段,这增加了大型 malloc() 失败的风险。这本质上是大块外部碎片的危害。
最佳拟合算法通常会找到仅大几个字节的拟合,从而导致只有几个字节的碎片,同时还会在需要时保存大块。此外,尽可能长时间地保持大块不变有助于缓存局部性并最大限度地减少 MMU 上的负载,最大限度地减少代价高昂的页面错误并为其他程序节省内存页面。
通过增加内部碎片(难以回收)和/或使用良好的查找表和搜索树,一个好的最佳拟合算法即使在管理大量小碎片时也能适当地保持其速度。
First-fit 和 next-fit 仍然面临着各自的搜索问题。如果在这些算法中没有良好的大小索引,他们仍然必须花时间在块中搜索适合的块。由于他们的“标准较低”,他们可能会使用简单的搜索更快地找到合适的人选,但一旦您添加智能索引,所有算法之间的速度就会变得更加接近。
我在过去 6 年中一直在使用和调整的那个可以在 O(1) 时间内为 >90% 的所有分配找到最合适的块。它利用一些策略直接跳到正确的块,或者开始非常接近,以便最小化搜索。由于它的性能和更有效地打包分配的能力,它已经在不止一次的情况下取代了现有的块池或首次拟合算法。
【讨论】: