为什么耗尽内存取决于对GC.GetTotalMemory的中间调用?

我编写的内存密集型程序内存不足:抛出OutOfMemoryexception。 在尝试减少内存使用量的过程中,我开始调用GC.GetTotalMemory(true)(将总内存使用量写入调试文件),这会触发垃圾收集。

出于某种原因,当调用此函数时,我不会再出现内存不足的exception。 如果我再次删除调用(保持其他所有内容相同),则会再次抛出exception。 根据我的理解,当内存压力增加时,会自动调用来收集垃圾,所以我不明白这种行为。

任何人都可以解释为什么只有在没有GC.collect调用时才抛出内存不足exception?

更新:

我正在使用VS 2010,但我正在将应用程序转移到框架3.5。 我相信碎片整理确实会导致我的问题。

我做了一些测试:当抛出exception时,对GC.gettotalmemory的调用告诉我我正在使用~800 * 10 ^ 6个字节。 但是,任务管理器告诉我应用程序使用的是1700 mb。 一个相当大的差异。 我现在计划只分配一次内存,并且永远不会释放任何大型数组但重用它们。 幸运的是,我的程序让我可以毫不费力地完成这个任务。

我通过做一些更智能的内存管理解决了这个问题。 特别是根据http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/上的建议使用CustomList

您的应用是否以完整CPU运行? 我很确定只有在应用程序空闲时才会发生自动垃圾收集。 否则,您必须运行手动循环。

我很确定内存不足并不会强制进行垃圾回收。 这可能听起来令人难以置信的不直观,但我认为这是有充分理由的。 它可以防止程序进入死亡螺旋,它不断地试图找到更多空间并将所有对象牢牢地插入到第2代。 从中很难再次恢复。

传递给GetTotalMemory()的真实参数强制进行完整的垃圾回收。 我猜想这会释放大对象堆中足够的空间以满足内存分配。 这当然只能工作一次。 如果你的程序一直在运行,吞噬超过1.5千兆字节的内存已经消耗掉,那么OOM就在眼前。 这个时候没有办法恢复。 幸存下来的OOM需要采取严厉措施。

你需要一个好的内存分析器来找出真正发生的事情。 项目中的非托管C ++始终是内存泄漏的沃土。 不受管理的那种,总是难以解决问题。

GC.Collect只是一个释放未使用内存的“建议” – 它不保证它的释放。

[编辑]看来,虽然多年前我学习JVM时曾经如此,但在.NET中可能不再是这种情况了。 MSDN Library说GC.Collect “强制所有代的立即垃圾收集。” 这里有关于此的好东西(对我而言)。

如果你有非托管资源占用大量内存,垃圾收集器将无法真正识别内存压力。 如果在终结器中清理这些资源,那么强制收集将导致释放那些非托管资源,而如果不强制收集,则垃圾收集器可能没有意识到它需要收集。

如果您正在执行大型非托管分配,则可以使用GC.AddMemoryPressure告知GC,因此在决定是否运行集合时可以将其考虑在内。

我浏览了Microsoft Connect网站,我看到的是人们正在提出相同声明的错误报告。 声称正在发生OutOfMemoryException ,可以通过定期调用GC.Collect来解决。 我看到一个报告 ,来自垃圾收集器团队的首席工程师回复并说在.NET 4.0中修复了一个错误,它应该解决大对象堆的碎片问题。 这就是为什么我问你正在使用什么版本。

您可能偶然发现了垃圾收集器中的错误。 与所有与GC相关的问题一样,这可能与版本有关。

我的建议是:

  • 确保您拥有最新的补丁和服务包
  • 重构代码,使其不像内存密集
  • 尽可能重用LOH对象而不是创建新对象
  • 如有必要,继续在战略要点使用GC.Collect作为解决方法