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

 风信子的春天R 发布于 2023-02-04 17:50

我广泛使用基于并行数组(SCG.SortedList看起来像Outer>)的嵌套数据结构和一个复制结构的计算引擎.结构的大小可能非常不同,其中许多都是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碎片是为速度和天真的内存压缩付出的代价.我无法全面测试但由于碎片导致多少内存实际上"泄漏",但从我读过的内容中我有理由怀疑是否存在任何泄漏.

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

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

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

    测量然后优化:)

    2023-02-04 17:52 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有