通常,如果您在托管应用程序中发现泄漏,这意味着某些对象没有被回收。常见的源包括:
- 事件处理程序:如果订阅者未被移除,则发布者将保留它。
- 静态变量
- 终结器:受阻止的终结器将防止终结器线程运行任何其他终结器,并因此防止这些实例被回收。
- 同样,死锁线程将保持其持有的所有根。当然,如果您有死锁线程,那么可能会在多个级别上影响应用程序。
要解决此问题,您需要检查托管堆。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将向您显示自上次检查以来添加/删除的实例数量。