gre*_*uit 11 .net garbage-collection windbg out-of-memory winforms
我们正在开发一个相当大的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[]
Run Code Online (Sandbox Code Playgroud)
应用程序在运行时会使用大量短缓冲区,但不会泄漏它们.使用!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)
Run Code Online (Sandbox Code Playgroud)
并检查本机终结器线程堆栈跟踪显示它未被阻止.
目前我不知道如何诊断为什么GC不收集数据(我相信它会喜欢,因为这个过程耗尽了内存......)
编辑:根据下面的输入,我读了一些关于大对象堆碎片的更多信息,似乎可能就是这种情况.
我已经看到一些建议为这种数据分配更大的内存块(在我的情况下是各种byte [])并自己管理这个区域的内存,但这似乎是一个相当hackish的解决方案,而不是我期望的那个解决不太特殊的桌面应用程序的问题.
碎片化问题是由于事实(至少是微软的许多人在博客中所说的那样)导致LOH上的对象在存在期间没有重新定位,这是可以理解的,但是一旦达到某些内存压力似乎是合乎逻辑的,例如获得OOM的威胁,应该进行重新安置.
在完全信任碎片之前唯一令我担心的是原因是LOH上有如此多的对象没有gcroot引用 - 这是因为即使LOH垃圾收集只是部分执行了吗?
我很乐意为我指出任何有趣的解决方案,因为目前我所知道的唯一一个是自定义管理某些预先分配的内存块.
欢迎任何想法.谢谢.
与往常一样,事情结果并没有什么不同。我们发现了一个用例,应用程序确实消耗了大量内存,最终会出现 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();
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2142 次 |
| 最近记录: |