如果你的应用程序必须对大型对象进行大量的分配/解除分配(> 85000字节),它最终会导致内存碎片,你的应用程序将抛出内存不足的异常。
是否有解决此问题的方法,还是CLR内存管理的限制?
答案 0 :(得分:7)
不幸的是,我见过的所有信息都只建议自己管理风险因素:重用大型对象,在开始时分配它们,确保它们的大小是彼此的倍数,使用替代数据结构(列表) ,树)而不是数组。这只是给了我另一个想法,创建一个非片段列表,而不是一个大型数组,分成更小的。数组/列表似乎是最常见的罪魁祸首IME。
这是一篇关于它的MSDN杂志文章: http://msdn.microsoft.com/en-us/magazine/cc534993.aspx,但其中没有那么多用处。
答案 1 :(得分:3)
CLR的垃圾收集器中有关大对象的事情是它们在不同的堆中进行管理。 垃圾收集器使用一种称为“压缩”的机制,它基本上是常规堆中对象的碎片和重新链接。 问题是,由于“压缩”大型对象(复制和重新链接它们)是一个昂贵的过程,GC为它们提供了不同的堆,永远不会被压缩。
另请注意,内存分配是连续的。这意味着如果您分配对象#1然后分配对象#2,对象#2将始终放置在对象#1之后。
这可能是导致你获得OutOfMemoryExceptions的原因。
我建议你看看Flyweight,Lazy Initialization和Object Pool等设计模式。
如果您怀疑某些大型物体已经死亡并且由于您的控制流程中的缺陷而未被收集,您也可以强制进行GC收集,从而导致它们在准备收集之前达到更高代
答案 2 :(得分:2)
程序总是在OOM上轰炸,因为它要求一大块内存太大,从来没有因为它完全耗尽了所有虚拟内存地址空间。你可能会说LOH变得支离破碎是一个问题,但是很容易争辩说程序使用的虚拟内存太多了。
一旦程序超出了可分配虚拟内存的一半(一千兆字节),现在是时候考虑让它的代码变得更聪明,这样它就不会占用太多内存。或者将64位操作系统作为先决条件。后者总是更便宜。它也不会从你的口袋里出来。
答案 3 :(得分:1)
Is there any solution to this problem or is it a limitation of CLR memory management?
除了重新考虑您的设计之外,没有解决方案。并且不是CLR的问题。请注意,非托管应用程序的问题是相同的。事实上,应用程序同时使用了太多的内存以及在内存中存在“不利”的段。如果必须指出一些外部罪魁祸首,我宁愿指向操作系统内存管理器,它当然不会压缩其虚拟机地址空间。
CLR在免费列表中管理LOH的自由区域。在大多数情况下,这是对碎片可以做的最好的事情。但是,对于非常大的对象,每个LOH段的对象数量减少 - 我们最终每个段只有一个对象。并且这些对象位于vm空间中的位置完全取决于OS的内存管理器。这意味着,碎片主要发生在操作系统级别 - 而不是在CLR上。这是堆碎片经常受到监督的一个方面,它不是.NET的责任。 (但事实也是如此,在that article中很好地证明,管理方面也可能出现碎片。)
已经命名了常用解决方案:重用大型对象。到目前为止,我没有遇到任何情况,这种情况无法通过适当的设计来完成。然而,它有时可能很棘手,因此可能很昂贵。
答案 4 :(得分:0)
我们在多个线程中处理图像。由于图像足够大,这也会因内存碎片而导致OutOfMemory异常。我们试图通过使用不安全的内存并为每个线程预分配堆来解决问题。不幸的是,由于我们依赖于几个库,因此这并没有完全帮助:我们能够在我们的代码中解决问题,而不是第三方。
最终我们用流程替换了线程,让操作系统付出了艰苦的努力。操作系统很久以前就为内存碎片构建了一个解决方案,因此忽略它是不明智的。
答案 5 :(得分:-1)
我在一个不同的答案中看到LOH可以缩小尺寸:
Large Arrays, and LOH Fragmentation. What is the accepted convention?
” ... 现在,话虽如此,如果LOH的末端区域完全没有活物,那么LOH的尺寸会缩小,所以唯一的问题是如果你将物体留在那里很长时间(例如应用的持续时间)。 ... “
除此之外,您可以使程序在32位系统上以高达3GB的扩展内存运行,在64位系统上以高达4 GB的速度运行。 只需在链接器或此post build事件中添加标志/ LARGEADDRESSAWARE:
调用“$(DevEnvDir).. \ tools \ vsvars32.bat” editbin / LARGEADDRESSAWARE“$(TargetPath)”
最后,如果您计划长时间运行程序包含大量大型对象,则必须优化内存使用情况,甚至可能必须重用已分配的对象以避免垃圾收集器在概念上类似,使用实时系统。