对象不是垃圾收集,但不包含gcroots

时间:2009-03-02 19:16:32

标签: asp.net debugging windbg

在这里遇到我们的网络应用程序的棘手问题。 (Asp.net 2.0 Win server 2008)

我们对网站的内存使用量增长和增长,即使我希望它保持在相当静态的水平。 (我们有少量数据存储在状态中。)

想要找出问题所在,我运行了一个System.GC.Collect();几次,采取内存转储,然后将此内存转储加载到WinDbg。

当我执行DumpHeap -Stat时,我会在特定类型中获得一个非常大的数字,这些数字会在内存中徘徊。

0000064280580b40 713471 79908752 PaymentOption

所以,为这种类型做一个DumpHeap -MT,我得到一堆对象引用。挑选一些随机数,我做了一个!gcroot,命令回来报告没有引用它。

对我来说,这正是GC应该收集这些项目的时候,但由于某种原因,它们一直处于劣势。

任何人都可以解释可能发生的事情吗?

7 个答案:

答案 0 :(得分:4)

您可以尝试在Windbg中使用sosex.dll,这是为帮助.NET调试而编写的扩展。有一个名为!refs的命令,类似于!gcroot,因为它会显示所有引用对象的对象,并且它将显示它也引用的所有对象。

在作者网站上的示例中,!refs用于对象,输出如下:

0:000> !refs 0000000080000db8
Objects referenced by 0000000080000db8 (System.Threading.Mutex):
0000000080000ef0         32    Microsoft.Win32.SafeHandles.SafeWaitHandle 

Objects referencing 0000000080000db8 (System.Threading.Mutex):
0000000080000e08         72    System.Threading.Mutex+<>c__DisplayClass3
0000000080000e50         64    System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode

答案 1 :(得分:2)

少数事情:

  1. GC.Collect无法帮助您进行任何调试。垃圾收集器已经被调用:如果有任何对象可供收集,它就已经发生了。
  2. 服务器上的空闲内存是浪费内存。你确定内存正在“泄露”,还是仅仅是框架决定它可以保留更多的东西以备不时之需或保留更多内存以便更快地访问?在这种情况下,我怀疑你是在泄漏记忆,但是需要仔细检查。
  3. 这听起来像是你不期望保留对PaymentOption对象的引用。也许在某个地方静态收藏?还是单独的线程?

答案 2 :(得分:2)

PaymentObject是否有机会实现终​​结器?它是否调用STA COM对象?

我很想看到!finalizequeue的输出,看看堆上显示的对象的数量是否等于可能等待最终确定的数量。输出应该看起来像这样:

generation 0 has 57 finalizable objects (0409b5cc->0409b6b0)
generation 1 has 55 finalizable objects (0409b4f0->0409b5cc)
generation 2 has 0 finalizable objects (0409b4f0->0409b4f0)
Ready for finalization 0 objects (0409b6b0->0409b6b0)

如果准备完成对象的数量继续增长,并且您的某些垃圾收集正在发生(通过perfmon计数器确认),那么它可能是一个阻塞的终结器线程。您可能需要在流程的生命周期内(在回收之前)拍摄多个快照以进行确认。我通常依靠三个幻数,只要该网站受到某种负载。

终结器中的错误可能会阻止终结器线程并阻止对象被收集。

如果PaymentOption对象调用旧的STA COM对象,那么本文ASP.NET Hang and OutOfMemory exceptions caused by STA components可能指向正确的方向。

答案 3 :(得分:1)

并非没有关于您的应用程序的更多信息。但是很久以前我们遇到了一些讨厌的记忆问题。你使用ASP.NET缓存吗?正如雷蒙德·陈(Raymond Chen)所说,“糟糕的缓存策略无法从内存泄漏中获益。”

查看另一个工具 - CLRProfiler.exe - 它将帮助您遍历对象引用树以查看对象的根目录。这也很好:link text

你以前听过这个 - 如果你要GC.Collect,那就错了。

答案 4 :(得分:1)

是否在异步进程中创建了PaymentOption对象?我记得有些事情,如果你不打电话给EndInvoke,你可能会遇到这样的问题。

答案 5 :(得分:1)

我自己一直在调查同一个问题,并且问为什么没有收集过没有引用的对象。

大于85,000字节的对象存储在大对象堆中,从中可以较少地释放内存。

http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

单个PaymentOption可能不是那么大,但它们是否包含在集合中,还是基于类似DataSet的东西?您应该选择PaymentOption / collection / DataSet的几个实例,然后使用sos!objsize命令来查看它们的大小。

不幸的是,这并没有真正回答这个问题。我想我可以信任.net框架来处理需要时释放未使用的内存。但是我看到运行我正在查看的应用程序的工作进程使用了​​大量内存,即使服务器上的内存看起来很紧张。

答案 6 :(得分:0)

仅供参考,.NET 4中的SOS支持一些可能有帮助的新命令,即!gcwhere(定位异议的生成; sosex的gcgen)和!findroots(执行它所说的内容)锡; sosex's!refs)

两者都记录在SOS documentationmentioned on Tess Ferrandez's blog