4

我有一个分配大量小对象和数组的 C# 控制台应用程序。这些对象的生命周期很短,很快就会被垃圾收集器清理掉。对于“为什么需要分配这么多寿命短的对象,你应该避免这种情况”的问题:该程序用于繁重的 AI 任务,目前没有明显的方法来解决这个问题。

这是问题所在:

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

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

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

  • 优化代码(仅限发布)
  • 检查算术上溢/下溢(仅限调试)
  • 调试信息:完整的调试信息(debug)/没有调试信息(release)

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

似乎已经提出了类似的问题,但它似乎与我的问题不同。

4

2 回答 2

5

如果终于查明原因。我的错。

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

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

我提出了答案并且没有结束这个问题,因为它可能对其他人有用。

于 2014-05-31T13:28:18.480 回答
0

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

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

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

通常,当程序消耗所有可用内存时,某些东西会保留对对象的引用,以使 GC 无法释放它们。

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

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

于 2014-05-31T05:48:10.723 回答