64位环境下的堆碎片问题

14

在过去,当我处理长时间运行的 C++ 守护程序时,我不得不处理堆碎片问题。像保留我的大型分配池这样的技巧是必要的,以避免耗尽连续的堆空间。

64 位地址空间是否仍然存在这个问题? 对于我来说,性能不是问题,因此我更喜欢简化我的代码,不再处理类似缓冲池之类的事情。有没有人对这个问题有任何经验或故事?我正在使用 Linux,但我想许多相同的问题也适用于 Windows。

3个回答

9
这是否仍然是64位地址空间的问题?
不,这不再是一个问题。
你是正确的,这在32位系统上是个问题,但在64位系统上已经不再是问题了。
虚拟地址空间在64位系统上非常大(目前在今天的x86_64处理器上为2^48字节,并随着新的x86_64处理器的推出逐渐增加到2^64),所以由于碎片化而耗尽连续虚拟地址空间实际上是不可能(除了一些高度人为制造的情况)。
(64 "只是" 32 的两倍,这是导致人们认为64位地址空间在某种程度上大约是32位地址空间的两倍的常见的直觉误区。事实上,完整的64位地址空间比32位地址空间大400亿倍)。
换句话说,如果你的32位守护程序需要一周时间才能碎片化到无法分配x个字节块的阶段,那么至少需要花费1000年的时间来碎片化今天的x86_64处理器的48位地址空间,并且需要8000万年才能碎片化未来计划中的完整64位地址空间。

2
堆碎片化在64位系统下和32位系统一样严重。如果您发出许多具有不同生命周期的请求,则会获得一个碎片化的堆。不幸的是,64位操作系统并没有真正解决这个问题,因为它们仍然无法将小块的空闲内存移动到一起以形成更大的连续块。
如果您想处理堆碎片化问题,仍然需要使用相同的老技巧。
唯一能帮助解决这个问题的方法是如果有足够大的内存量,您永远不会使其碎片化,64位操作系统才会有所帮助。

3
这是不正确的。所描述的问题涉及以碎片化方式填满32位系统的大约4GB虚拟地址空间,导致即使总共有足够的虚拟内存可用,也无法分配大的连续块。在64位系统上,由于虚拟地址空间大约增加了2^32(实际上是从2^16增加到2^32),这几乎是不可能的。 - Andrew Tomazos
1
此外,即使您正在讨论子页面大小的块,几乎所有现代操作系统和C库都使用__小块分配器__,通过在进程启动时透明地分配内存池/桶来将相同大小的小块紧密地压缩在一起,从而避免了小规模的碎片化。 - Andrew Tomazos
3
@MichaelKohne:不正确。虚拟内存和物理内存之间的映射(页表)可以并且确实会以页面大小的粒度(通常在x86上为4096字节)动态更改。在大于此页面大小的比例尺下,物理内存的连续性在很大程度上是无关紧要的,因为虚拟内存中的连续页面范围可以有效地映射到等大小的动态非连续顺序物理页面集合中。这就是虚拟内存的全部意义。在小于页面大小的比例尺下,小块分配器(malloc实现)在用户空间中处理它(使用如前所述的池)。 - Andrew Tomazos
@user1131467 - 从这个角度来看,首先要记住内存是有物理限制的。在64位进程中,内核可以选择使用真正惊人的大量磁盘来呈现进程具有荒谬数量的内存的幻象,但它并不是无限的。它只是我们已经使用到几乎没有人会看到碎片问题的技术的延伸。换句话说:64位对于解决问题并不重要,重要的是愚蠢数量的内存和一个聪明的分配器如何相对于页面边界进行分配。 - Michael Kohne
5
抱歉,但你并不知道你在说什么。 "进程视图" 的内存地址处于虚拟地址空间 (VAS) 中。我们正在讨论的是 VAS 的内存碎片问题,而不是物理内存耗尽的问题 - 与磁盘交换无关。VAS 不必由磁盘支持,空页面只需在页表中标记为空即可,没有后备存储器。VAS 碎片化是在32位系统上的一个问题,因为VAS足够小而变得碎片化,而在64位系统上,VAS非常大,无法被碎片化。 - Andrew Tomazos
显示剩余3条评论

0

如果您的进程确实需要千兆字节的虚拟地址空间,那么升级到64位确实可以立即消除解决方法的需求。

但是值得计算一下您预计进程将使用多少内存。如果只有大约1GB或更少的内存,即使是疯狂的碎片也不会让您用完32位地址空间 - 内存泄漏可能是问题所在。

(顺便说一句,Windows更加严格,因为它为每个进程保留了不礼貌的地址空间)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接