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

标签 c# .net memory-management

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

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

在深入了解 LOH 碎片之后,我可以推断在我的情况下,我将会遇到 LOH 碎片,因为在我的流程/计算中经常发生的情况正是 great article about LOH fragmentation by Andrew Hunter 中所描述的:创建、随机增量增长、复制数组...

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

  • 请专家确认一下评论中的说法对于 .NET 托管内存是否正确? 64 位系统上的 LOH 碎片是否真的不值得担心?在大多数情况下,由于地址空间非常大?在编写这个问题时,我发现了 a confirmation for C++ ,所以这个问题专门针对 .NET 中的托管内存。

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

链接文章的评论如下:

Page Cache Posted by: TruePath Posted on: Thursday, November 24, 2011 at 6:50 AM Message:

Either you are using an old OS/cpu that doesn't support 64 bit addressing (or a cheap version of win 7) or MS has radically failed to take proper advantage of 64 bit addressing.

Win 7 ultimate lets a process address up to 192 gigs, snow leopard 16TB and linux 32 exabytes. You are never going to realistically chew through that entire address space in the LOH. Thus the strategy that MS implemented for allocation sounds like the right one and it's only some artificial limit that is getting in the way.

Remember just because you've allocated a page of memory doesn't mean the OS has to always back that up with a page of RAM. The OS is perfectly free to swap pages out to disk or leave blank pages unbacked. The LOH should be allocating an integer number of psges (4K) for each object. When an object in the LOH is freed by the GC the pages allocated to it should be returned to the OS so they no longer need any backing store and don't take up disk space or strain the page allocation structures.

Indeed, the LOH on a 64 byte system should be substantially faster and less resource intensive than the normsl heaps since no objects are ever copied and all objects get an integer number of pages. Unlike the regulsr heap fragmentation isn't really an issue since this gets handled for you by the hardware supported page tables. Indeed it might be optimal to never GC the LOH until virtual memory limits are hit.

True such a strategy would end up writing huge parts of your LOH out to disk but that happens in a second concurrent process. As long as you aren't allocating objects in the LOH and dirtying their pages faster than your disk can write them you shouldn't see any slowdown except a minor competion for disk IO and a small impact of larger OS page maps. The system won't thrash because most of those pages really are free and thus are never again read back from disk. Without a GC marking objects in the LOH no false page access by the GC occurs. Thus the LIFO algorithm employed by the page cache is a pretty decent GC that trades GC overhead for some disk writes and (maybe) an occasional read.

I guess it would be superior to hold the GC metadata and any embedded pointers for objects in the LOH on a separate page from the rest of the data (it can precede it if you want and the rest of the page can be used for other heaps/metadata) so the GC can still free the pages in the LOH without triggering any unneeded page loads. Since objects in the LOH have few if any pointer members/elements (or are all pointers and must be scanned by the GC anyway) one can segregate those from the data and get the best of both worlds: no disk writes and no false page loads for the GC.

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

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

最佳答案

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 上。

测量然后优化:)

关于c# - 与 .NET 上的 32 位平台相比,64 位平台上的大对象堆碎片是否是一个问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21036376/

相关文章:

android - 在 iOS 和 Android 中使用 .NET 公开的 OData 模式

android - android中应用程序的RAM分配

c# - 如何更改 ObjectListView 中一个单元格的颜色?

c# - 在 C# 中删除 QueryString 变量

c# - 为什么 Windows 窗体应用程序中的窗体类被声明为部分的?

c# - EntityFramework CreatedOn 属性只能在 SaveChanges 方法中设置

c# - 如何保证调用 DateTime.UtcNow 两次将在 .NET 中给出两个不同的时间

c# - 不可变值在 IIS 中发生变异?

objective-c - AVCaptureSession 启动内存警告

iOS内存管理一头雾水