C# 如何诊断System.OutOfMemoryException?

C# 如何诊断System.OutOfMemoryException?,c#,out-of-memory,C#,Out Of Memory,我在上周五下班前运行了一些代码,周一我在这里,它因OutOfMemoryException而停止。我估计整个过程需要进行数百亿次计算,所以这不是一个小任务 我甚至不知道如何开始修复此问题。 有什么指示吗?啊。。。这是您确实不希望在应用程序中出现的异常之一。我们在项目中也遇到了这个异常,我们使用.net内存分析器来检查内存使用和泄漏。在某种程度上,我们可以减少退出内存异常的频率。下面是profiler的链接- 好吧,考虑到您提供的内容或缺乏内容,我在这里只能提供一些一般性的想法:首先,显而易见的答

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

我甚至不知道如何开始修复此问题。

有什么指示吗?

啊。。。这是您确实不希望在应用程序中出现的异常之一。我们在项目中也遇到了这个异常,我们使用.net内存分析器来检查内存使用和泄漏。在某种程度上,我们可以减少退出内存异常的频率。下面是profiler的链接-


好吧,考虑到您提供的内容或缺乏内容,我在这里只能提供一些一般性的想法:首先,显而易见的答案是查看异常本身包含的信息,这应该让您了解代码分配失败的地方

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


除此之外,您可能希望在问题中包含更多信息。您正在分配什么类型的对象,何时分配,是否使用本机资源等。

Eric Lippert的这篇文章可能会有所帮助:

您需要确定是什么导致了它,它来自何处,然后您可以研究重写该部分,以减少内存使用,也许可以把它分成几部分,然后释放一些资源


尝试在某个地方引入带有时间戳的日志(执行步骤1,执行步骤2),以帮助识别失败的地方,然后如果不明显,您可以询问有关减少内存依赖性的更具体问题。

有几件事可以尝试

首先,使用内置在Windows中的性能监视器进行粗略检查。为Process->Private bytes used添加一个计数器,并相应调整缩放比例,以便快速查看内存使用情况。您将能够更快地识别泄漏,而不是等待一夜。您可以尝试的另一个计数器位于.NET内存中,但由于我的性能监视器当前不允许我添加.NET计数器(ARGH!),因此我无法确切地告诉您子选择是什么

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

你是在递归调用你的函数吗?每次通过函数可以分配成吨的内存,虽然可能不会破坏堆栈,但会耗尽堆

有什么记忆可以让你更快地摆脱吗?即使不使用非托管代码,也可以标记托管代码,以便垃圾回收器更快地将其删除,然后它就不会进入Gen2堆。实现IDisposable,然后对不再需要挂起的类调用Dispose()

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

如果所有这些都失败了,请尝试RedGate中的内存分析器,如ANTS5。它在我的代码中发现了另一个bug,我注册了一个事件处理程序,但没有注销它。结果,我的代码挂起了与此事件相关的所有位和块,不幸的是,它也挂起了内存位。:)


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

我想您正在将计算结果存储在一个容器中,如列表或数组。尝试将结果写入文件,而不是存储在内存中。

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

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

  • 如果怀疑泄漏源来自某些本机代码,可以通过发出以下命令进行验证:
    !eeheap-gc
    。输出将告诉您托管堆需要多少内存。如果进程的私有工作集大小远远大于托管堆的大小,那么您可能有一个本机泄漏(或者,可能是由于某种原因,您产生了大量线程,因此由于线程堆栈,您的空间不足)[您可以通过发出以下命令来检查进程中生成了多少线程:
    ~*
    ,或:
    !threads
    以仅显示托管线程])如果你正在重复分配非常大的数组,你可能会遇到。如果你是这样的话,你可能需要考虑是否可以重用工作数组来防止重新分配和随后的碎片化。超出了您假设的有效期。

    您对其中一个有什么建议吗?计算结果产生时,您正在做什么的可能重复?汉斯:不是真的——他想知道如何开始appr