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