我还有另一个活跃的问题HERE关于一些可能涉及LOH碎片的无望记忆问题,可能还有其他未知因素。
我现在的问题是,接受的做事方式是什么? 如果我的应用程序需要在Visual C#中完成,并且需要处理大型数组到int [4000000]的调整,那么我怎么能不注意垃圾收集器拒绝处理LOH ?
似乎我被迫将任何大型数组全局化,并且从不在它们周围使用“new”这个词。所以,我留下了带有“maxindex”变量的不合适的全局数组,而不是由函数传递的整齐大小的数组。
我一直被告知这是不好的做法。还有什么替代方案?
System.GC.CollectLOH("Seriously")
的曲调是否有某种功能?
有没有办法将垃圾收集外包给System.GC以外的其他东西?
无论如何,处理大型(> 85Kb)变量的普遍接受的规则是什么?
答案 0 :(得分:26)
首先,垃圾收集器 收集LOH,所以不要立即被它的早期吓到。收集第2代时会收集LOH。
不同之处在于LOH没有被压缩,这意味着如果你有一个具有长寿命的物体,那么你将有效地将LOH分成两个部分 - 前面的区域和该物体之后的区域。如果这种情况继续发生,那么你可能会遇到这样的情况,即长期存在的对象之间的空间不足以进行后续分配,并且.NET必须分配越来越多的内存才能放置大对象,即LOH变得支离破碎。
现在,话虽如此,如果LOH的末端区域完全没有活物,那么LOH的尺寸会缩小,所以唯一的问题是如果你把物体放在那里很长时间(例如应用的持续时间) )。
从.NET 4.5.1开始,可以压缩LOH,请参阅GCSettings.LargeObjectHeapCompactionMode属性。
避免LOH碎片的策略是:
编辑:双数组的LOH阈值似乎是8k。
答案 1 :(得分:8)
这是一个老问题,但我认为用.NET中引入的更改更新答案并没有什么坏处。现在可以对大对象堆进行碎片整理。显然,首选应该是确保做出最佳设计选择,但现在有这个选项很好。
https://msdn.microsoft.com/en-us/library/xe0c2357(v=vs.110).aspx
“从.NET Framework 4.5.1开始,您可以通过在调用Collect方法之前将GCSettings.LargeObjectHeapCompactionMode属性设置为GCLargeObjectHeapCompactionMode.CompactOnce来压缩大对象堆(LOH),如以下示例所示。”
可以在System.Runtime命名空间
中找到GCSettingsGCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
答案 2 :(得分:7)
首先要想到的是将阵列分成较小的阵列,这样它们就无法到达GC所需的内存来放入LOH。你可以把数组吐成10,000个较小的数组,并根据你传递的索引器构建一个知道要查看哪个数组的对象。
现在我还没有看到代码,但我也会质疑你为什么需要一个大的数组。我可能会考虑重构代码,以便所有这些信息不需要一次存储在内存中。
答案 3 :(得分:6)
你弄错了。你不需要有4000000的数组大小,你绝对不需要调用garbace收集器。
这允许您仅使用一个重定向访问基本上所有元素。并且,由于单个阵列较小,碎片不是问题......
...如果是......那么REUSE页面。不要把它们扔掉处理掉,把它们放在一个静态的“PageList”上并先从那里拉出来。所有这些都可以在你的课堂上透明地完成。
真正的好处是这个List在内存使用方面非常动态。您可能想要调整holder数组(重定向器)的大小。即使没有,它也只是每页512kbdata。
第二级数组每个字节基本上有64k - 一个类是8字节(每页512kb,32位256kb),或每个结构字节64kb。
技术上:
打开 INT [] 成 INT [] []
根据需要确定32位或64位是否更好;)两者都有优点和缺点。
处理一个像这样的大型阵列在任何语言中都是不可思议的 - 如果你愿意,那么......基本上......在程序启动时分配,永远不会重新创建。只有解决方案。
答案 4 :(得分:0)
在问题如何产生方面,我对上述答案进行了详细阐述。 LOH的碎片不仅取决于长寿命的对象,而且如果你有多个线程并且每个都创建大型列表进入LOH的情况,那么你可能会遇到第一个线程的情况需要增加它的List但是下一个连续的内存位已经被第二个线程中的List占用,因此运行时将为第一个线程List分配新的内存 - 留下一个相当大的漏洞。这是目前在我继承的一个项目上发生的事情,因此即使LOH大约为4.5 MB,运行时总共有117 MB的可用内存,但最大的可用内存段是28MB。
如果没有多个线程,这种情况可能发生的另一种方式是,如果你在某种循环中添加了多个列表,并且每个列表都扩展到最初分配给它的内存之外,那么随着它们的增长,每个都会超越另一个他们分配的空间。
有用的链接是:https://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/
仍在为此寻找解决方案,一个选项可能是使用某种池化对象并在执行工作时从池中请求。如果您正在处理大型阵列,那么另一种选择是开发自定义集合,例如集合的集合,这样你就不会只有一个巨大的列表,而是将它分解成更小的列表,每个列表都避免使用LOH。
答案 5 :(得分:0)
这是一个古老的问题,但是对于.NET Standard 1.1(.NET Core,.NET Framework 4.5.1+),还有另一种可能的解决方案:
使用ArrayPool<T>
包中的System.Buffers
,我们可以对数组进行池化处理,以避免出现此问题。