繁体   English   中英

在.NET上,64位平台上的大对象堆碎片与32位相比是一个问题吗?

[英]Is Large Object Heap fragmentation on 64-bit platforms an issue at all compared to 32-bits, on .NET?

我广泛使用基于并行数组的嵌套数据结构( SCG.SortedList<K,V> ,看起来像Outer<K, Inner<K_delta,V>> )和一个复制结构的计算引擎。 结构的大小可能非常不同,其中许多都是LOH,因为 - 即使它们已经嵌套 - 内部或外部的长度足够长,值大多为双倍(对于双倍,限制为1000个元素)每个数组后,它进入LOH,至少在x86上)。

嵌套数据结构的用例是在长时间运行的过程中累积高频数据。 我可以使用它作为缓冲区来聚合数据,只保留聚合所需的最小窗口(通过删除较旧的内部列表并修剪外部列表),但这种复制/修剪本身可能会产生LOH碎片问题并占用更多内存而不是保存它。

在深入阅读LOH碎片后,我可以推断,在我的情况下,我会有LOH碎片 ,因为在我的过程/计算中经常发生的事情正如所描述的那样,例如, 一篇关于Andrew Hunter的LOH碎片伟大文章 :创造,成长通过随机增量,复制数组......

但是,我无法理解64位平台上的LOH碎片是否存在问题? 在对同一篇文章的最后评论中,一位评论者认为,在64位平台上,LOH碎片几乎不是问题,因为地址空间太大,实际上很难用尽它,而内存漏洞/空页不支持通过真实记忆。 (另一个选择是MS在设计GC时失败了)。

  • 有些专家可以确认评论中的陈述是否适用于.NET托管内存? 在大多数情况下,由于极大的地址空间,64位系统上的LOH碎片是不是应该担心? 在写这个问题时我发现了对C ++的确认 ,所以这个问题专门用于.NET中的托管内存。

  • 第二个问题是它如何适用于在64位系统上运行的32位进程 这两者是否同样适用,或者如果存在LOH碎片,32位进程可能会快速耗尽内存? (例如,对于Excel加载项,即使在64位系统上,由于传统加载项很长时间,我也被限制为32位架构)

链接文章的评论如下:

页面缓存发布者:TruePath发布日期:2011年11月24日星期四上午6:50消息:

您使用的是不支持64位寻址的旧操作系统/ CPU(或win 7的廉价版本),或者MS从根本上无法正确利用64位寻址。

Win 7终极版允许一个进程地址高达192演出,雪豹16TB和Linux 32艾字节。 你永远不会真实地咀嚼LOH中的整个地址空间。 因此,MS为分配实施的策略听起来像是正确的策略,而且只是一些人为的限制正在阻碍。

请记住,因为您已经分配了一页内存并不意味着操作系统必须始终使用一页RAM来备份。 操作系统完全可以自由地将页面交换到磁盘或保留空白页面。 LOH应该为每个对象分配整数个psges(4K)。 当GC释放LOH中的对象时,应该将分配给它的页面返回给操作系统,这样它们就不再需要任何后备存储,也不会占用磁盘空间或使页面分配结构变形。

实际上,64字节系统上的LOH应该比规范堆更快且资源更少,因为没有对象被复制并且所有对象都获得整数页。 与regulsr堆不同,碎片不是真正的问题,因为硬件支持的页表会为您处理。 实际上,在达到虚拟内存限制之前,从不GC到LOH可能是最佳的。

确实,这样的策略最终会将LOH的大部分内容写入磁盘,但这会在第二个并发进程中发生。 只要你没有在LOH中分配对象并且弄脏他们的页面的速度比你的磁盘写的速度快,你就不应该看到任何减速,除了磁盘IO的小竞争和较大的OS页面映射的小影响。 系统不会崩溃,因为大多数页面确实是免费的,因此永远不会再从磁盘读回。 如果LOH中没有GC标记对象,则不会发生GC的错误页面访问。 因此,页面缓存使用的LIFO算法是一个相当不错的GC,它可以在某些磁盘写入和(可能)偶尔读取时交换GC开销。

我想将LOH中的对象的GC元数据和任何嵌入式指针保存在与其余数据不同的页面上会更好(如果你愿意,它可以在它之前,页面的其余部分可以用于其他堆/ metadata)因此GC仍然可以释放LOH中的页面而不会触发任何不需要的页面加载。 由于LOH中的对象几乎没有任何指针成员/元素(或者都是指针并且必须由GC扫描),因此可以将这些对象与数据隔离并获得两全其美:无磁盘写入且无错误页面加载对于GC。

更新假设存在LOH碎片。 问题是关于内存地址空间碎片对x64平台上的实际内存的影响,以及它在x64上运行的32位进程中的工作方式。

问题不在于如何避免它/处理它以及使用什么数据结构(文章讨论了这个)。 我已经做了很多测试,发现嵌套排序列表比不可变结构快5倍以上,并且通过在内部列表中使用键delta(uint16 vs int64)将我的数据压缩c.40%,而IntMap / AVL树每个键/值对占用70/50字节的开销。 非常逼真的1000万对我更喜欢嵌套SL。 因此,预期/可能的LOH碎片是为速度和天真的内存压缩付出的代价。 我无法全面测试但由于碎片导致多少内存实际上“泄漏”,但从我读过的内容中我有理由怀疑是否存在任何泄漏。

CLR中的垃圾收集器没有任何不良设计。 问题在于,为了对LOH进行碎片整理,您需要创建空间,然后重新排序并压缩对象。 对于大型对象,重新排序可能会移动几个大型对象,而内存中获得的收益很少(例如,如果您说100个对象,每个对象大小为0.5MB,则可能需要复制并重新排序200MB内存才能压缩此对象记忆空间。 在这个链接上有一个很好的解释这个现象

64位CLR具有与LOH相同的大小阈值(因为这是基于实际应用选择的),并且它不会比32位CLR中的问题更多。 如果您转向.Net 4.0+,它将对LOH算法进行改进,以防止内存不足并改善堆栈中空洞的重用,这将有所帮助。 .Net 4.5甚至压缩了LOH LOH压缩MSDN ,这可以抵消处理大型阵列的自定义应用程序的大多数问题。

由于地址空间的大小,使用64位将有一个优势。 但是,这一讨论都没有否定软件的质量设计。 垃圾收集器是应用程序的一个方面,可能导致它运行缓慢。 您应该查看您的算法和数据结构,以确保您获得所需的效率增益。 如果您接近应用程序的限制并且看到碎片问题,也许您应该调查使用除数组之外的集合和/或限制数组的大小,以便它们不在LOH上分配。

测量然后优化:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM