首先,您必须了解实际发生的情况。在这种情况下,我的第一个工具总是WinDbg。
http://www.windbg.org/
http://en.wikipedia.org/wiki/WinDbg
要将其与托管/.NET 代码一起使用,您需要使用 SOS(Son of Strike)扩展:
http://msdn.microsoft.com/en-us/library/bb190764.aspx
http://blogs.msdn.com/b/johan/archive/2007/11/13/getting-started-with-windbg-part-i.aspx
所以,一旦你将 WinDbg 附加到你的 w3wp.exe 进程,你要做的第一件事就是弄清楚你的堆中实际有什么:
!dumpheap -stat
这将为您提供内存中所有当前“活动”对象的格式良好的视图,以及它们有多少,它们占用了多少字节,按对象类型分组。
现在,大对象堆 (LOH) - 通常,当对象被垃圾收集时,会发生压缩,有点像对硬盘驱动器进行碎片整理。这使新对象的分配保持快速有效。问题是,大型物体不容易压缩——一切都必须四处移动以容纳它们。因此,任何占用超过 85000 字节的东西都会被卡在一个特殊的地方,称为大对象堆。这个堆没有被压缩,所以随着时间的推移,就像硬盘驱动器一样,会发生碎片,在堆中留下未使用的间隙,这导致运行时需要更多空间等。
所以,让我们让 windbg 告诉我们 LOH 中的内容:
!dumpheap -stat -min 85000
这将向您展示大对象堆中的实际内容——其中一些对象可能会直接出现在您面前,例如 List 或 MyClass[]。
重要提示:如果您在大对象堆中看到的东西是有意长期存在的(例如,记录器的静态实例),那么这可能不是真正的问题。但是,您确实想尝试减少其中短期/经常创建的对象的数量,以减少碎片。
所以,我推荐一个 SOS 探索的备忘单:
http://windbg.info/doc/1-common-cmds.html
http://windbg.info/doc/2-windbg-az.html
有趣的命令:
!gcroot <address> <- will show you what object is "rooting" another
!CLRStack <- show current managed call stack
!dumpobj <address> <- show information about object at address
但我一直以来最喜欢的是:
bp clr!SVR::gc_heap::allocate_large_object "!CLRStack; g;"
它在 CLR 在大对象堆上分配对象时使用的实际内部调用上设置断点,当命中该对象时,将转储完整的堆栈跟踪,然后继续。