如何诊断System.OutOfMemoryException?

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

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

好吧,考虑到你提供的内容,或者缺乏相应的内容,我只能在这里提供一些一般性的想法:首先,明显的答案是看一下exception本身所包含的信息,它应该给你一个想法你的代码分配失败的地方。

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

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

Eric Lippert的这篇文章可能有所帮助: “Out of Memory”不指物理内存

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

  1. 使用procdump (包含在Sysinternals Suite中 )可以在进程达到某种不合理的大小时对其进行完整的内存转储。
  2. 将转储加载到WinDbg中,并使用.loadby sos clr加载SOS调试扩展
  3. 使用以下命令: !dumpheap -stat以查看哪种类型的对象占用了大部分内存。
  4. 使用!dumpheap -type 转储对象列表(泄漏类型)
  5. 选择几个实例,然后发出命令: !gcroot 。 输出应告诉您哪个对象仍然包含对它的引用,以及为什么不释放该对象。

如果您怀疑泄漏源来自某些本机代码,您可以通过发出以下命令来validation这一点: !eeheap -gc 。 输出将告诉您托管堆占用多少内存。 如果你的进程的私有工作集大小远远大于托管堆的大小,那么你可能手上有本机泄漏(或者,也许是因为某种原因你已经产生了很多线程,所以你是由于线程的堆栈而耗尽空间[你可以通过发出这个命令检查你的进程中产生了多少线程: ~* ,或: !threads只显示被管理的!threads ])。

你可以试试几件事。

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

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

你是递归地调用你的函数吗? 你可能每次通过你的函数分配大量的内存,虽然你可能不会破坏堆栈,但你将用尽堆。

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

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

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

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

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

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

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

http://memprofiler.com/

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

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