通常,如果托管应用程序中有泄漏,则意味着没有收集到某些东西。常见来源包括
事件处理程序:如果订阅者未删除,则发布者将保留它。
静力学
终结器:阻塞的终结器将阻止终结器线程运行任何其他终结器,从而阻止收集这些实例。
类似地,死锁线程将保留它所持有的任何根。当然,如果您有可能会在多个级别上影响应用程序的死锁线程。
要解决此问题,您需要检查托管堆。WinDbg + SOS(或 PSSCOR)会让你这样做。该!dumpheap -stat
命令列出整个托管堆。
您需要了解堆上预期的每种类型的实例数。一旦你发现一些看起来很奇怪的东西,你可以使用该!dumpheap -mt <METHOD TABLE>
命令列出给定类型的所有实例。
下一步是分析这些实例的根源。随机选择一个并!gcroot
对其进行操作。这将显示该特定实例是如何植根的。寻找事件处理程序和固定对象(通常表示静态引用)。如果您在那里看到终结器队列,则需要检查终结器线程在做什么。为此使用!threads
and!clrstack
命令。
如果该实例的一切看起来都很好,那么您将转到另一个实例。如果这没有产生任何结果,您可能需要返回再次查看堆并从那里重复。
其他泄漏源包括: 未卸载的程序集和大对象堆的碎片。SOS/PSSCOR 也可以帮助您找到这些,但我现在将跳过详细信息。
如果你想了解更多,我推荐Tess 的博客。我还制作了几个视频,介绍如何使用 WinDbg + SOS(此处和此处)。
如果您可以选择在进程运行时对其进行调试,我建议您使用PSSCOR而不是 SOS。PSSCOR 本质上是 SOS 源的一个私有分支,已通过附加命令进行了增强,并且许多现有的 SOS 命令也得到了改进。例如,该命令的 PSSCOR 版本!dumpheap
有一个非常有用的 delta 列,这使得解决内存泄漏问题变得更加容易。
为了使用它,您需要启动您的进程,附加 WinDbg 并加载 PSSCOR 并执行!dumpheap -stat
. 然后让进程再次运行,以便进行分配。中断执行并重复命令。现在 PSSCOR 将显示自上次检查以来添加/删除的实例数。