我广泛使用基于并行数组(SCG.SortedList<K,V>
,看起来像Outer<K, Inner<K_delta,V>>
)的嵌套数据结构和一个复制结构的计算引擎。结构的大小可能非常不同,其中许多都是LOH,因为 - 即使它们已经嵌套 - 内部或外部的长度足够长,值大多为双倍(对于双倍,限制为1000个元素)每个数组后,它进入LOH,至少在x86上。
嵌套数据结构的用例是在长时间运行的进程中累积高频数据。我可以使用它作为缓冲区来聚合数据,只保留聚合所需的最小窗口(通过删除较旧的内部列表并修剪外部列表),但这种复制/修剪本身可能会产生LOH碎片问题并占用更多内存而不是保存它。
在深入了解LOH碎片之后,我可以推断,在我的情况下,我将会有LOH碎片,因为我的流程/计算中经常发生的事情恰好是所描述的,例如a {{ 3}}:通过随机增量创建,增长,复制数组......
但是,我不能了解64位平台上的LOH碎片是否存在问题?在同一篇文章的最后评论中,评论者认为在64位平台上LOH碎片几乎是一个非问题,因为地址空间太大,实际上很难用尽,而内存空洞/空页面不是真实内存支持。 (另一个选择是MS在设计GC时失败了。)
有些专家可以确认评论中的该陈述是否适用于.NET托管内存吗? 64位系统上的LOH碎片是不是应该担心的事情在大多数情况下由于极大的地址空间?在写这个问题时我发现great article about LOH fragmentation by Andrew Hunter,所以这个问题专门针对.NET中的托管内存。
第二个问题是它如何适用于在64位系统上运行的32位进程?这两者是否同样适用,或者如果存在LOH碎片,32位进程可能会快速耗尽内存? (例如,对于Excel加载项,即使在64位系统上,由于传统加载项很长时间,我也被限制为32位架构)
链接文章的评论如下:
页面缓存 发布者:TruePath 发布于:2011年11月24日星期四上午6:50 消息:
你要么使用旧的 OS / cpu不支持64位寻址(或廉价版本的 win 7)或MS从根本上未能充分利用64位 寻址。
Win 7终极让一个进程地址高达192演出,雪豹 16TB和linux 32 exabytes。你永远不会真实地咀嚼 通过LOH的整个地址空间。因此战略 实现分配的MS听起来像是正确的,只是它的唯一 一些人为的限制正在阻碍。
请记住,因为您已经分配了一页内存并不意味着 操作系统必须始终用一页RAM来支持它。操作系统是 完全免费将页面交换到磁盘或留下空白页 无背衬。 LOH应该分配整数个psges(4K) 为每个对象。当LOH中的对象被GC释放时 分配给它的页面应该返回给操作系统,以便它们不再存在 需要任何后备存储,不要占用磁盘空间或拉紧页面 分配结构。
实际上,64字节系统上的LOH应该快得多 因为没有对象,所以资源密集程度低于规范堆 永远复制,所有对象都获得整数页。不像 由于这种情况,regulsr堆碎片并不是一个真正的问题 由硬件支持的页表为您处理。确实可能 在达到虚拟内存限制之前,最好不要使用LOH GC。
真正的这样一种策略最终会把你LOH的大部分内容写下来 到磁盘,但这发生在第二个并发进程中。只要 你不是在LOH中分配对象并弄脏他们的页面 比你的磁盘写得快,你不应该看到任何减速 除了磁盘IO的小型竞争和较大的OS的小影响 页面地图。系统不会因为大部分页面真的而摔倒 是免费的,因此永远不会再从磁盘读回。没有GC 标记LOH中的对象不会发生GC的错误页面访问。从而 页面缓存使用的LIFO算法是一个相当不错的GC 一些磁盘写入和(可能)偶尔的GC交易 读。
我认为保持GC元数据和任何嵌入式内容都会更好 LOH中对象的指针与其余部分分开 数据(如果你想要它可以在它之前,而页面的其余部分可以 用于其他堆/元数据)所以GC仍然可以释放页面 在LOH中,不会触发任何不需要的页面加载。自从对象 在LOH中几乎没有任何指针成员/元素(或者都是 无论如何必须由GC扫描指针)可以将这些指针隔离开来 从数据中获取两全其美:没有磁盘写入和没有磁盘写入 GC的错误页面加载。
更新:让假设存在LOH碎片。问题是关于内存地址空间碎片对x64平台上的实际内存的影响,以及它在x64上运行的32位进程中的工作方式。
问题不在于如何避免它/处理它以及使用什么数据结构(文章讨论了这个)。我已经做了很多测试,发现嵌套排序列表比不可变结构快5倍以上,并且通过在内部列表中使用键delta(uint16 vs int64)将我的数据压缩c.40%,而IntMap / AVL树每个键/值对占用70/50字节的开销。非常逼真的1000万对我更喜欢嵌套SL。因此,预期/可能的LOH碎片是为速度和天真的内存压缩付出的代价。我无法全面测试,但实际上有多少内存&#34;泄漏&#34;由于碎片,但从我所读到的,我有理由怀疑是否有任何泄漏。
答案 0 :(得分:5)
CLR中的部分垃圾收集器没有错误的设计。问题在于,为了对LOH进行碎片整理,您需要创建空间,然后重新排序并压缩对象。对于大型对象,重新排序可能会移动几个大型对象,而内存中获得的收益很少(例如,如果您说100个对象,每个对象大小为0.5MB,则可能需要复制并重新排序200MB内存才能压缩此对象记忆空间。对这种现象有一个很好的解释at this link
64位CLR具有与LOH相同的大小阈值(因为这是基于实际应用程序选择的),并且它不会比32位CLR中的任何问题更多。如果您转向.Net 4.0+,它将对LOH算法进行改进,以防止内存不足并改善堆栈中空洞的重用,这将有所帮助。 .Net 4.5甚至压缩了LOH LOH compaction MSDN,这可以消除处理大型数组的自定义应用程序的大多数问题。
由于地址空间的大小,使用64位会有一个优势。但是,这一讨论都没有否定软件的质量设计。垃圾收集器是应用程序的一个方面,可能导致它运行缓慢。您应该查看您的算法和数据结构,以确保您获得所需的效率增益。如果您正在接近应用程序的限制并且看到碎片问题,那么您可能应该调查使用除数组之外的集合和/或限制数组的大小,以便它们不会在LOH上分配。
然后测量优化:)