64位焊盘中的堆碎片 过去,当我处理长时间运行的C++守护进程时,我不得不处理堆碎片问题。为了防止连续堆空间耗尽,有必要使用一些技巧,比如保留大量分配的池

64位焊盘中的堆碎片 过去,当我处理长时间运行的C++守护进程时,我不得不处理堆碎片问题。为了防止连续堆空间耗尽,有必要使用一些技巧,比如保留大量分配的池,c++,64-bit,heap-fragmentation,C++,64 Bit,Heap Fragmentation,这仍然是64位地址空间的问题吗?Perf对我来说并不重要,所以我更愿意简化代码,不再处理缓冲池之类的事情。有没有人有过关于这个问题的经验或故事?我正在使用Linux,但我想许多同样的问题也适用于Windows 如果您的进程确实需要千兆字节的虚拟地址空间,那么升级到64位确实可以立即消除对变通方法的需要 但计算出您希望进程使用多少内存是值得的。如果它仅在1GB或更少的范围内,那么即使是疯狂的碎片也无法让您耗尽32位地址空间——内存泄漏可能是问题所在 (顺便说一句,Windows的限制性更大,因为它

这仍然是64位地址空间的问题吗?Perf对我来说并不重要,所以我更愿意简化代码,不再处理缓冲池之类的事情。有没有人有过关于这个问题的经验或故事?我正在使用Linux,但我想许多同样的问题也适用于Windows

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

但计算出您希望进程使用多少内存是值得的。如果它仅在1GB或更少的范围内,那么即使是疯狂的碎片也无法让您耗尽32位地址空间——内存泄漏可能是问题所在


(顺便说一句,Windows的限制性更大,因为它在每个进程中为操作系统保留了不适当的地址空间)。

堆碎片在64位和32位下同样是一个问题。如果您使用不同的生命周期发出大量请求,那么您将得到一个碎片堆。不幸的是,64位操作系统在这方面并没有真正的帮助,因为它们仍然不能真正地洗牌少量的空闲内存来生成更大的连续块

如果要处理堆碎片,仍然必须使用相同的老技巧

64位操作系统在这方面的唯一帮助是,如果有一些内存“足够大”,您永远不会将其分割

这仍然是64位地址空间的问题吗

不,这还不是一个问题

您是对的,这在32位系统上是一个问题,但在64位系统上不再是问题

64位系统上的虚拟地址空间非常大(目前x86_64处理器上的虚拟地址空间为2^48字节,随着新x86_64处理器的推出,虚拟地址空间将逐渐增加到2^64字节),因此几乎不可能因碎片而耗尽连续的虚拟地址空间(除了一些人为设计的极端情况)

(这是一个常见的直觉错误,因为64是“仅”双32,这导致人们认为64位地址空间在某种程度上是32位地址空间的两倍。事实上,完整的64位地址空间是32位地址空间的40亿倍。)


换句话说,如果您的32位守护进程花了一周的时间分割成一个无法分配x字节块的阶段,那么至少需要1000年的时间来分割今天的x86_64处理器的48位地址空间,要将未来计划中的完整64位地址空间分割成碎片需要8000万年。

好吧,如果要将我的32位空间分割成碎片需要一周的时间,我会说64位空间“足够大”,我永远不会将其分割成碎片。假设64位操作系统实际使用了整个虚拟空间。所以我想,和往常一样,答案是“这取决于你的操作系统和应用程序”……这是不正确的。所描述的问题是指以分段方式填充32位系统的~4G字节虚拟地址空间,以便即使总体上有足够的可用虚拟内存,也无法再分配大型连续块。在64位系统上,这几乎是不可能的,因为虚拟地址空间要大2^32倍(2^16增加到2^32)。此外,即使您谈论的是子页大小的块,几乎所有现代操作系统和c库都使用一个小的块分配器,以避免小规模的碎片,通过在进程启动时透明地分配内存池/存储桶,将大小相同的小块压缩在一起。@MichaelKohne:不正确。虚拟内存和物理内存(页表)之间的映射可以而且确实会以页大小的粒度(在x86上通常是4096字节)动态变化。在大于此页面大小的比例下,物理内存的连续性在很大程度上是不相关的,因为虚拟内存中的连续页面范围可以有效地映射到相同大小的动态非连续无序物理页面集。这就是虚拟内存的全部意义。在小于页面大小的范围内,小数据块分配器(malloc impl)在userland中处理它(使用前面描述的池)。@MichaelKohne:对不起,你不知道你在说什么。内存地址的“进程视图”位于虚拟地址空间(VAS)中。我们讨论的是VAS的内存碎片问题,而不是phys内存耗尽的问题—交换到磁盘与此无关。VAS不必由磁盘备份,空页面只需在页面表中标记为空,无备份存储。在32位系统上,VAS碎片化是一个问题,因为VAS小到可以碎片化,而在64位系统上,VAS大到不能碎片化。