OutOfMemory,但没有许多对象的gcroots

时间:2009-10-10 22:24:39

标签: .net winforms windbg out-of-memory garbage-collection

我们正在开发一个相当大的Windows窗体应用程序。在几个客户的计算机中,它经常因OutOfMemory异常而崩溃。在异常之后获取应用程序的完全内存转储(从UnhandledException处理程序调用clrdump)后,我使用“.NET Memory Profiler”和windbg对其进行了分析。

Memory Profiler在实时对象实例中只显示了130MB。有趣的是,对于许多对象类型,已经显示了大量无法访问的实例(例如,22000个无法访问的Byte []实例)。在本机内存统计中,它在数据的所有堆中总共为127MB(可以),但是表示第2代堆中的133MB不可达,大堆中则为640MB(不行!)。

使用windbg分析转储时,上述统计数据已确认:

!dumpheap -stat
..... acceptable object sizes...
79330a00   467216     30638712 System.String
0016d488     4804    221756612      Free
79333470    27089    574278304 System.Byte[]

应用程序在运行时会使用大量短缓冲区,但不会泄漏它们。使用!gcroot测试许多Byte []实例最终没有根。显然,大多数这些数组都无法访问,如内存分析器所示。

只是为了确保一切正常,!finalizequeue显示没有对象等待最终确定

generation 0 has 138 finalizable objects (18bd1938->18bd1b60)
generation 1 has 182 finalizable objects (18bd1660->18bd1938)
generation 2 has 75372 finalizable objects (18b87cb0->18bd1660)
Ready for finalization 0 objects (18bd1b60->18bd1b60)

并检查本机终结器线程堆栈跟踪显示它未被阻止。

目前我不知道如何诊断为什么GC不收集数据(我相信它会喜欢因为这个过程耗尽了内存......)

编辑:根据下面的输入,我会阅读更多有关大对象堆碎片的内容,而且似乎可能就是这种情况。

我已经看到一些建议为这种数据分配更大的内存块(在我的情况下是各种字节[])并自己管理这个区域的内存,但这似乎是一个相当hackish的解决方案,而不是一个我希望通过不那么特殊的桌面应用程序解决问题。

碎片化问题是由于事实(至少是微软的许多人在博客中所说的那样)导致LOH上的对象在存在期间没有重新定位,这是可以理解的,但是一旦达到某些内存压力似乎是合乎逻辑的如获得OOM的威胁,应该进行重新安置。

在完全相信碎片化之前唯一让我担心的是原因是LOH上有如此多的对象没有gcroot引用 - 这是因为即使LOH垃圾收集只是部分执行了吗?

我很乐意为我指出任何有趣的解决方案,因为目前我所知道的唯一一个是自定义管理某些预先分配的内存块。

欢迎任何想法。 感谢。

4 个答案:

答案 0 :(得分:4)

卫生部受到分割。 This article提供了分析和解决它的基本方向 也许您可以发布一些显示那些byte []缓冲区的“典型”用法的代码?

答案 1 :(得分:2)

通常事情变得与众不同。我们找到了一个用例,其中应用程序确实消耗了大量内存,最终会使用OOM。在我们发现之前我们得到的转储中有什么奇怪的是有很多没有gcroot的对象 - 我不明白它为什么不被释放并用于新的分配?然后它告诉我,当OOM发生时可能发生的事情 - 堆栈被解开并且拥有内存的对象不再可达,然后执行了转储。所以这就是为什么看起来有很多内存可以用于GC。

我在调试版本中所做的 - 检索真实的内存状态转储 - 是创建一个Threading.Timer来检查是否可以分配一些相当大的对象 - 如果它无法分配,这表明我们在OOM附近,并且它是获取内存转储的好时机。代码如下:

private static void OomWatchDog(object obj)
{
 try                          
 {
   using(System.Runtime.MemoryFailPoint memFailPoint = 
          new System.Runtime.MemoryFailPoint(20))
   {
   }
 }
 catch (InsufficientMemoryException)
 {
   PerformDump();
 }
}

答案 2 :(得分:1)

有时Image.FromFile(“非图像文件”)会抛出OutOfMemoryException。零字节文件就是这样一个文件。

答案 3 :(得分:1)

如果您认为LOH是问题,那么在LOH分配上有一个断点可能会指向正确的方向。你可能会做这样的事情

bp mscorwks!gc_heap :: allocate_large_object“!clrstack; .echo *********大对象堆的分配***********; g”