这个问题有解决方案吗?还是CLR内存管理的限制?
Is there any solution to this problem or is it a limitation of CLR memory management?
除了重新考虑设计,没有其他解决方案。这不是CLR的问题。请注意,对于非托管应用程序,问题是相同的。这是由于应用程序在内存中同时使用太多内存,并且在内存中处于“不利”的段中。如果必须指向某些外部罪犯,我宁愿指向操作系统内存管理器,它(当然)不会压缩其vm地址空间。
CLR在自由列表中管理LOH的自由区域。在大多数情况下,这是防止碎片化的最佳方法。但是,由于对于真正大的对象,每个LOH段中的对象数量会减少-最终我们只有一个对象每个段。而那些对象在vm空间中的位置完全取决于操作系统的内存管理器。这意味着,碎片主要发生在操作系统级别上,而不是CLR。这是堆碎片化经常被忽视的方面,.NET并不为此负责。(但也是真的,碎片化也可能发生在托管端,就像那篇文章中演示的那样。)
常见的解决方案已经被提出:重复使用您的大型对象。到目前为止,我还没有遇到过任何情况,在这种情况下,通过适当的设计无法完成此操作。然而,有时可能会很棘手,因此可能会很昂贵。
我们正在多线程处理图像。由于图像足够大,这也导致了由于内存碎片而引起的OutOfMemory异常。我们尝试通过使用不安全的内存和为每个线程预分配堆来解决问题。不幸的是,这并没有完全帮助我们,因为我们依赖于几个库:我们能够在我们的代码中解决问题,但不能解决第三方问题。
最终,我们用进程替换了线程,并让操作系统来完成艰苦的工作。操作系统早就为内存碎片构建了解决方案,因此忽视它是不明智的。
我在另一个答案中看到LOH可以缩小:
“... 话虽如此,如果其末尾区域完全没有活动对象,则LOH可以缩小大小,因此唯一的问题是如果您将对象留在那里很长时间(例如应用程序的持续时间)。 ...”
除此之外,您可以在32位系统上扩展内存至3GB,在64位系统上扩展内存至4GB。 只需在链接器中添加标志/LARGEADDRESSAWARE或此后生成事件:
call "$(DevEnvDir)..\tools\vsvars32.bat" editbin /LARGEADDRESSAWARE "$(TargetPath)"
最后,如果您计划长时间运行具有大量大型对象的程序,则必须优化内存使用,并且甚至可能必须重复使用已分配的对象以避免垃圾收集器,这类似于实时系统的工作概念。