【发布时间】:2009-10-10 22:24:38
【问题描述】:
我们正在开发一个相当大的 Windows 窗体应用程序。在几个客户的计算机中,它经常因 OutOfMemory 异常而崩溃。在异常发生后(从 UnhandledException 处理程序调用 clrdump)获得应用程序的完整内存转储后,我使用“.NET Memory Profiler”和 windbg 对其进行了分析。
内存分析器在活动对象实例中仅显示 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 不收集数据的原因(我相信它会很乐意,因为进程内存不足..)
编辑:根据下面的输入,我阅读了有关大型对象堆碎片的更多信息,似乎情况可能如此。
我已经看到一些建议为这种数据分配更大的内存块(在我的情况下是各种字节 [])并自己管理该区域的内存,但这似乎是一个相当老套的解决方案,而不是一个我希望解决一个不那么特别的桌面应用程序的问题。
碎片问题是由于 LOH 上的对象在存在期间没有重定位这一事实(至少这是许多 Microsoft 的博客中所说的)造成的,这是可以理解的,但一旦达到一定的内存压力似乎是合乎逻辑的,比如有OOM的威胁,应该进行重定位。
在完全相信碎片是原因之前唯一让我担心的是,LOH 上的这么多对象没有 gcroot 引用 - 这是因为即使是 LOH 垃圾收集也只是部分执行?
我很乐意为我指出任何有趣的解决方案,因为目前我所知道的唯一一个是一些预分配内存块的自定义管理。
欢迎任何想法。 谢谢。
【问题讨论】:
标签: .net winforms windbg out-of-memory garbage-collection