15

在这里我们的网络应用程序遇到了一个棘手的问题。(Asp.net 2.0 赢服务器 2008)

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

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

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

0000064280580b40 713471 79908752 付款选项

所以,为这种类型做一个 DumpHeap -MT,我得到了一堆对象引用。选择其中的一个随机数,我执行 !gcroot 并且命令返回报告没有对其进行引用。

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

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

4

7 回答 7

5

您可以尝试在 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
于 2009-03-08T20:56:50.200 回答
2

一些事情:

  1. GC.Collect 不会帮助您进行任何调试。垃圾收集器已经被调用:如果有任何对象可用于收集,它就已经发生了。
  2. 服务器上的空闲内存是浪费的内存。您确定内存正在“泄漏”,还是只是框架决定它可以在内存中保留更多内容或保留更多内存以便更快地访问?在这种情况下,我怀疑您正在泄漏内存,但需要仔细检查。
  3. 听起来您不希望保留对 PaymentOption 对象的引用。也许某个地方的静态集合?还是单独的线程?
于 2009-03-02T19:26:12.417 回答
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)

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

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

如果 PaymentOption 对象调用旧版 STA COM 对象,那么本文由 STA 组件导致的 ASP.NET Hang 和 OutOfMemory 异常可能指向正确的方向。

于 2009-08-14T05:04:08.597 回答
1

并非没有有关您的应用程序的更多信息。但是很久以前我们遇到了一些令人讨厌的内存问题。你使用 ASP.NET 缓存吗?正如 Raymond Chen 喜欢说的那样,“糟糕的缓存策略与内存泄漏无法区分”。

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

你以前听过这个——如果你必须使用 GC.Collect,那就错了。

于 2009-03-02T19:24:18.457 回答
1

PaymentOption 对象是在异步过程中创建的吗?我记得一些事情,如果你不调用 EndInvoke,你可能会遇到这样的问题。

于 2009-03-02T19:26:05.630 回答
1

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

大于 85,000 字节的对象存储在大对象堆中,从该堆中释放内存的频率较低。

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

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

不幸的是,这并不能真正回答这个问题。我喜欢认为我可以信任 .net 框架来负责在需要时释放未使用的内存。但是,我看到运行我正在查看的应用程序的工作进程正在使用大量内存,即使服务器上的内存看起来非常紧张。

于 2009-07-28T10:15:12.870 回答
0

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

两者都记录在SOS 文档中,并在 Tess Ferrandez 的博客中提及

于 2010-03-01T21:40:49.173 回答