.NET中用于处理OutOfMemoryException的try/catch块的设计模式

5

我有一个处理大量数据的应用程序,我想可能会有时会抛出OutOfMemoryException异常(半年来,我没有遇到过任何异常,但我只是想了解一下)。根据我的调查,发生这种异常后,我无法继续执行我的程序。

有没有什么好的模式来处理这样的异常,特别是与IDisposable类一起使用的情况?


3
设计一个应用程序来处理OutOfMemoryException 可能是你最后想做的事情,不是指“不要做”,而是在这之前还有其他事情需要做。内存从来都不是无限的资源,良好设计的应用程序应该始终能够运行,只要一些最低标准得到满足,但如果你的应用程序可以利用大量的内存来加速操作,那么它应该被设计成这样,而不是被设计成 “需要大量内存”。您确定已经采取了足够的措施来预防OutOfMemoryException吗? - Lasse V. Karlsen
@Lasse V. Karlsen,我从来没有在我的项目中遇到过这样的异常 - 我只是好奇,是否有任何提示应该知道。 - VMAtm
@VMAtm,请运行一个内存分析工具,确保您没有泄漏内存。这是最重要的事情要做。 - myermian
1
@myermian 我对此并没有任何问题 - 我只是对这样的事情感到好奇。 - VMAtm
@VMAtm:有更多值得好奇的有用事情 - 实际发生的事情,而不是这个还没有发生的事情。 - John Saunders
3个回答

5
在真正的OOM场景中(在x86上比在x64上更有可能),你几乎注定会失败。几乎任何事情都会引起分配,因此最好的选择是尽快、优雅地死亡,对系统造成最小的损害。
由于这种情况并没有发生,所以不必过度紧张,但是避免问题要比处理问题更好:
- 使用流式数据API而不是将所有内容缓存到内存中 - 重复使用缓冲区等资源 - 避免使用庞大的数组/列表等(事实上,导致OOM的最常见方式是请求一个巨大(但单一)的数组)——例如,分块数组比2D数组更可扩展(即使在x64上,单个数组的最大大小也有硬限制) - 考虑如何处理稀疏数据 - 如果您从外部源读取大量字符串,请考虑使用自定义符号表,这样您就不必拥有20,000个不同的常见字符串副本(例如国家名称)。 - 注意释放对象的时机 - 避免在误操作下延长对象的生命周期,特别是通过事件订阅(容易意外延长生命周期)

我对这个话题知之甚少,但是你的第一段似乎与 Is “Out Of Memory” A Recoverable Error? 的答案相矛盾。如果我有所遗漏,请您澄清一下,谢谢! - Alexander Malakhov

4

没有好的模式,OOM是一个可随时发生的令人讨厌的异常。这使它几乎成为异步异常。处理它的唯一机会是当您的代码被结构化为同时分配大量内存时。因此,您将有一些机会回退并撤销程序状态,就好像什么都没有发生过。

您需要设计您的程序,使其永远不需要分配超过所有可用虚拟内存的一半,即32位机器上的1 GB。龙生活在那个数量之外,您的程序将在分配90 MB或更少的内存时失败,即使还有500 MB未使用的虚拟内存。这是由地址空间碎片化引起的问题。如果您经常跨越此阈值,则需要切换到64位操作系统。


我分配的内存不到1GB就出现了内存不足错误。我怀疑COM字典对象可能会在大对象堆上分配小的东西,导致严重的碎片化问题。我对Microsoft处理LOH的理念感到困惑,但事实就是这样。 - supercat
地址空间碎片化总是在身边破坏你的一天。刚开始启动程序后,你永远无法分配超过约550 MB的内存。从那里很快就会走下坡路。 COM不会从大对象堆(LOH)分配内存,而是使用自己的堆。 - Hans Passant
我本来以为它会工作的,但出于某些原因,在我的应用程序中运行COM DLL在将该DLL从使用Collection更改为使用Dictionary时导致内存失控;当该DLL作为自己的进程运行时,任务管理器组合的DLL + 应用程序的内存使用量总计约为两者一起运行时应用程序使用量的三分之一。 - supercat

2
前面两个答案是正确的,也是有帮助的建议,但是它们没有提到一件事情——负载测试。如果你担心在某些负载下你的应用程序可能会耗尽内存,请对其进行测试以检查该负载(最好是更大的负载)下的表现。
在我之前的工作中,我使用了这样一个工具(HP's Performance Center),它不仅证明了检查我们系统错误和限制的重要性,而且还能识别瓶颈和昂贵的操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接