如何诊断System.OutOfMemoryException?

时间:2010-09-13 12:47:16

标签: c# out-of-memory

我上周五在离开工作之前运行了一些代码,我星期一在这里,它以OutOfMemoryException停止。我估计整个过程需要进行数百亿次计算,所以这不是一项小任务。

我甚至不知道如何开始解决这个问题 有什么指针吗?

8 个答案:

答案 0 :(得分:9)

Eric Lippert的这篇文章可能有所帮助:“Out Of Memory” Does Not Refer to Physical Memory

答案 1 :(得分:8)

嗯,鉴于你提供的内容或缺乏,我只能在这里提供一些一般性的想法:首先,显而易见的答案是看看例外本身所包含的信息,这应该给出你知道你的代码分配失败的地方。

其次,您使用内存分析来更好地了解应用程序中发生了什么 - 我是dotTrace的用户,但可能有免费的替代方案。

除了这个普遍的假设,您可能希望在您的问题中包含更多信息。您分配的是什么类型的对象,何时分配,是使用本机资源等。

答案 2 :(得分:5)

最简单,最直接的解决方案是:

  1. 使用procdump(包含在Sysinternals Suite中),当进程达到一些不合理的大小时,可以对进程进行完整的内存转储。
  2. 将转储加载到WinDbg中,并使用.loadby sos clr
  3. 加载SOS调试扩展程序
  4. 使用以下命令:!dumpheap -stat以查看哪种类型的对象占用了大部分内存。
  5. 使用!dumpheap -type <MY_TYPE>
  6. 转储对象列表(泄漏类型)
  7. 选择几个实例,然后发出命令:!gcroot <OBJ_ADDRESS>。输出应告诉您哪个对象仍然保留对它的引用,以及为什么不释放该对象。
  8. 如果您怀疑泄漏源来自某些本机代码,您可以通过发出以下命令来验证这一点:!eeheap -gc。输出将告诉您托管堆占用多少内存。如果你的进程的私有工作集大小远远大于托管堆的大小,那么你可能手上有本机泄漏(或者,或许是因为某种原因你产生了很多由于线程的堆栈,所以你的空间不足[你可以通过发出这个命令来检查你的进程中产生了多少线程:~*,或者:!threads只显示管理的])。

答案 3 :(得分:3)

您可以尝试几种方法。

首先,使用内置于Windows的性能监视器进行粗略的传递。为Process添加计数器 - &gt;使用专用字节,并相应地调整缩放,以便快速浏览内存使用情况。而不是一夜之间等待,你将能够更快地识别泄漏。您可以尝试的另一个计数器可以在.NET内存中找到,但由于我的性能监视器目前还没有让我添加.NET计数器(ARGH!),我无法准确地告诉您子选项是什么。

您是否通过计算功能中的非托管代码分配了内存?如果是这样,你是否正确处理了内存?您编写的以这种方式分配内存的任何代码都应该实现IDisposable,您还应该对这些类调用Dispose()以确保它们得到清理。

你是递归地调用你的函数吗?你可能每次都通过你的函数分配大量的内存,虽然你可能没有炸掉堆栈,但你将用完堆。

你有什么记忆可以早点摆脱吗?即使您没有使用非托管代码,您仍然可以标记托管代码,以便垃圾收集器尽快删除它,然后它不会在Gen2堆中结束。实现IDisposable,然后在不再需要挂起的类上调用Dispose()。

你在使用线程吗?你可能在你的线程中分配内存,如果你没有通过调用EndInvoke来正确处理错误,你就会以这种方式孤立内存。

如果所有其他方法都失败了,请尝试使用RedGate中的ANTS5等内存分析器。它在我的代码中发现了另一个错误,我注册了一个事件处理程序,但没有取消注册。结果,我的代码挂在了与此事件相关的所有碎片上,不幸的是,这些碎片也悬挂在内存中。 :)

希望这会有所帮助。我最近经历了所有这些,这些是我现在能提出的最有用的提示。

答案 4 :(得分:2)

我知道你将计算结果存储在像列表或数组这样的容器中。尝试将结果写入文件而不是将其存储在内存中。

答案 5 :(得分:2)

如果要重复分配非常大的数组,可能会遇到Large Object Heap Fragmentation。如果是这种情况,您可能需要考虑是否可以重用工作数组来防止重新分配和后续碎片。我绝对建议你运行一个内存分析器,因为你更有可能在内存中留下一些东西,而不是你假设的有效期。

答案 6 :(得分:1)

Aaah ......这是您真正不想在应用程序中获得的例外情况之一。我们在项目中也得到了这个例外,我们使用.net内存分析器来检查内存使用和泄漏。在某种程度上,我们可以减少出现内存异常的频率。以下是探查者的链接 -

http://memprofiler.com/

答案 7 :(得分:1)

你需要确定导致它的原因,它来自哪里,然后你可以考虑重写那个部分,以便它使用更少的内存,也许可以将它分成几块,然后释放一些资源。

尝试使用时间戳在某处(执行步骤1,执行步骤2)引入日志以帮助确定失败的位置,然后您可以询问有关减少内存依赖性的更具体的问题(如果不明显的话)。