如何使用 dump 文件诊断内存泄漏?

75

我有一个 .NET 服务,正常的私有工作集大小约为80 MB。在最近的负载测试中,该进程达到了3.5 GB的内存使用,导致整个机器内存紧张(4 GB中使用了3.9 GB),并且在负载测试停止后很长时间没有释放内存。我使用任务管理器获取了进程的转储文件,并在Visual Studio 2010 SP1中打开它进行调试。

如何诊断内存问题?我可以使用dotTrace Memory 3.x来支持转储文件上的内存分析吗?如果不行,Visual Studio 2010 Premium的内存分析功能是否有用(我当前使用Professional版本)?WinDbg能帮忙吗?

更新: 新版的Visual Studio 2013 Ultimate可以使用转储文件本地诊断内存问题。请参阅此博客文章了解更多详细信息。


仅限于Visual Studio 2013旗舰版... - Samuel
@Samuel:真的吗?太悲伤和令人失望了... - Allon Guralnek
这是参考的 MSDN 文章:http://blogs.msdn.com/b/visualstudioalm/archive/2013/06/20/using-visual-studio-2013-to-diagnose-net-memory-issues-in-production.aspx。它指出,该选项的前提条件是 Ultimate 版本。令人失望的是,我认为它在 RC1 中可用,并已被推入 Ultimate 版本,这是一个相当昂贵的功能... - Samuel
通过以下答案,您可能还需要其他步骤来加载Windows SOS / CLR dll。我已经解决了这个问题,并在此添加了一份快速摘要步骤:http://stackoverflow.com/a/20692646/284598 - GaTechThomas
4个回答

130

安装WinDbg。您需要确保获取正确的版本x86或x64,具体取决于您的转储文件。这是一个指向x86下载链接

在此基础上,您需要确保获取了正确的转储文件。您可以使用任务管理器创建转储文件(右键单击进程->创建转储文件)。如果您正在使用64位,并且您的进程是x86,请使用32位版本的任务管理器(C:\Windows\SysWOW64\taskmgr.exe)来获取转储文件。有关获取转储文件的更多信息,请参见我的文章,例如如果您使用XP并且需要使用windbg来创建转储文件。

警告:学习曲线相对较陡峭,并且可能无法完全按照描述工作,请遇到任何问题时返回。

我假设您正在使用.NET4,因为您可以在Visual Studio中打开该转储文件。以下是一个非常简短的指南,以帮助您处理dmp文件:

1)运行WinDbg,将符号路径(文件->符号搜索路径)设置为

SRV*c:\symbols*http://msdl.microsoft.com/download/symbols

2) 打开Crash dump文件或将你的.DMP文件拖放到WinDbg中。

3) 在命令窗口中输入以下内容。

.loadby sos clr
(FYI,对于.NET 2,命令应该是.loadby sos mscorwks)
4) 然后输入这个。
!dumpheap -stat

这个列表展示了对象类型及其数量。看起来像这样:

enter image description here

您需要在应用程序上下文中分析它,看是否有任何异常情况。

windbg 还有更多功能,可以通过谷歌搜索获得更多信息。


2
这里有一个很好的工具,可以创建两个不同内存转储之间的差异并指示增长:http://thinkexception.blogspot.de/2010/06/tool-to-bompare-two-windbg-dumpheap.html - Samuel
5
使用命令"!dumpheap -stat -live"来避免查看已死亡对象。 - Silas Hansen
第三步出现了错误:“调用LoadLibrary(D:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos)失败,Win32错误0n126“指定的模块无法找到。”- 它默认在D:/驱动器上查找sos文件?手动指定C:/驱动器位置,如此答案中所述,可以解决问题。 - Taran

34
通常,如果您在托管应用程序中发现泄漏,这意味着某些对象没有被回收。常见的源包括:
- 事件处理程序:如果订阅者未被移除,则发布者将保留它。 - 静态变量 - 终结器:受阻止的终结器将防止终结器线程运行任何其他终结器,并因此防止这些实例被回收。 - 同样,死锁线程将保持其持有的所有根。当然,如果您有死锁线程,那么可能会在多个级别上影响应用程序。
要解决此问题,您需要检查托管堆。WinDbg + SOS(或PSSCOR)可以让您执行此操作。`!dumpheap -stat` 命令列出整个托管堆。
您需要了解期望在堆上每种类型的实例数。一旦找到看起来不正常的内容,您可以使用 `!dumpheap -mt ` 命令列出给定类型的所有实例。
下一步是分析这些实例的根源。随机选择一个实例,然后在其上执行!gcroot。这将显示该特定实例的根源。查找事件处理程序和固定对象(通常表示静态引用)。如果看到其中的终结器队列,则需要检查终结器线程正在执行什么操作。使用!threads!clrstack命令进行操作。
如果对该实例的所有内容都看起来正常,则继续移动到另一个实例。如果这没有产生任何结果,则可能需要返回并重新查看堆,并从那里重复。
其他泄漏来源包括:未卸载的程序集和大对象堆的碎片化。SOS/PSSCOR也可以帮助您找到这些问题,但现在我会跳过详细信息。
如果您想了解更多信息,我建议参考Tess的博客。我还制作了一些视频,介绍如何使用WinDbg + SOS(此处这里)。
如果您有调试运行过程的选项,我建议使用PSSCOR代替SOS。PSSCOR本质上是SOS源代码的私有分支,增加了其他命令,并且许多现有的SOS命令也得到了改进。例如,!dumpheap命令的PSSCOR版本具有非常有用的delta列,使内存泄漏的故障排除变得更加容易。
为了使用它,您需要启动进程,附加WinDbg并加载PSSCOR,然后执行!dumpheap -stat命令。接着让进程再次运行以进行分配。中断执行并重复该命令。现在,PSSCOR将向您显示自上次检查以来添加/删除的实例数量。

当你给一个负面评分时,请留下评论。谢谢。 - Brian Rasmussen

6
自2017.2版本起,JetBrains dotMemory支持使用其强大而惊艳的GUI分析Windows内存转储。

真的很棒! - Allon Guralnek
你只需要提供-ma键或dotMemory可能无法读取转储。 - chester89

0

1
非常有趣的指南。那些 P&P 的家伙确实能够制作出好东西。 - Allon Guralnek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接