仅在发布时编译时抛出OutOfMemoryException

时间:2014-05-28 20:14:31

标签: c# garbage-collection out-of-memory compile-mode

我有一个C#Console应用程序,它分配了许多小对象和数组。这些物体的使用寿命很短,很快就会被垃圾收集器清理干净。对于"为什么你需要分配这么多短生命对象的问题,你应该避免这个" :该程序用于繁重的AI任务,没有明显的方法现在就解决这个问题。

问题在于:

如果我在调试模式x86下运行该程序,它运行正常并在几分钟后完成所有处理。平均而言,它使用300-400 MB。

如果我采用完全相同的程序,但是在发布x86模式下编译并运行它,程序使用的内存会快速达到2GB(几秒钟内),因此它会抛出一个OutOfMemoryException(这是预期的行为因为它是32位应用程序)。在发布x64模式下进行编译并不能解决问题,它会快速使用计算机的所有内存(8GB),然后在内存分配失败时崩溃。

我使用SharpDevelop 4.3.3来构建应用程序。调试和发布模式之间的唯一区别是:

  • 优化代码(仅限发布)
  • 检查算术溢出/下溢(仅限调试)
  • 调试信息:完整调试信息(调试)/无调试信息(发布)

在所有情况下都没有附加调试器。程序非常简短,并且没有编译器指令可以使它在调试或发布时编译时运行方式不同。 没有明显的理由来解释这种行为。在发布模式下进行编译时,看起来垃圾收集器从未被触发(或者至少没有足够的时间)并且内存未被释放。

似乎已经问过similar question,但它似乎与我的问题不同。

2 个答案:

答案 0 :(得分:5)

如果终于找到了原因。我的错。

我有一个Debug.Assert()方法调用,它不仅执行一些检查,还执行了一些操作(例如:Debug.Assert(List.Remove()))。

我假设在两种情况下(发布和调试)都要执行Debug.Assert(),并且只有在调试模式下才会测试结果值,但这是错误的。在发布模式下进行编译时,Debug.Assert()调用将从代码中完全删除。

我提出了答案而不是关闭这个问题,因为它可能对其他人有用。

答案 1 :(得分:0)

您可以在代码中进行调用以强制垃圾收集器运行。例如:

    GC.Collect();
    GC.WaitForPendingFinalizers();

但是,我怀疑垃圾收集器实际上正在运行,所以这不会产生重大影响。最佳实践建议是不要试图超越垃圾收集器。我建议这只是为了证明问题不存在。

通常当程序占用所有可用内存时,某些东西会持有对象的引用,这样GC就无法释放它们。

面对这个问题,我会使用某种内存分析器来弄清楚什么没有被释放,以及什么是持有它。您可以使用各种.NET内存分析工具。 (我不确定它们在发布代码上有多好)。我可能会尝试使用JetBrains dotMemory的免费试用版。我没有亲自使用它,但我发现其他分析工具很有帮助。

顺便说一句,不要为使用依赖于许多短期物品的设计模式而道歉。这是一种完全可以接受的设计模式。有一些编程语言是编写代码的唯一方法。