1

我有一个应用程序非常广泛地使用托管和非托管对象。存在内存泄漏,我尝试了解如何修复它们。我使用了 SOS 扩展并运行 !objsize 命令。HANDLE(RefCnt=0) 是什么意思,我如何检查对象是否应该被 GC 收集?

DOMAIN(07E74830):HANDLE(RefCnt=1):9611400: sizeof(099290b8) =         1756 (       0x6dc) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=1):9611404: sizeof(09928a00) =         1764 (       0x6e4) bytes (BO.Account)
DOMAIN(07E74830):c:9611408: sizeof(099237c8) =        25760 (      0x64a0) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):961140c: sizeof(099c7fc8) =         4084 (       0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):9611410: sizeof(099c9148) =         4084 (       0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):9611414: sizeof(099ca2c8) =         4084 (       0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):9611418: sizeof(099cb448) =         4084 (       0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):961141c: sizeof(099cc630) =         4312 (      0x10d8) bytes (BO.Account)
4

1 回答 1

2

它们是垃圾收集句柄。在 .NET Framework 中以 GCHandle 类型公开。它们保留对托管对象的引用,超出了 GC 可以自行发现的正常对象。

有几种不同的类型,用于不同的目的。GCHandleType 枚举显示了其中的一些,弱引用和固定是常见用途。GCHandleType.Normal 在非托管互操作中很常见,它允许非托管代码保留对托管对象的引用,gcroot<>是它的包装器。

它们也被 CLR 使用,这些类型不被 GCHandleType 公开。在 SOS 输出中显示为“RefCnt”的那些是用于 COM 互操作的特殊类型,它保留对 RCW、运行时可调用包装器、COM 接口的托管包装器对象的引用。这些句柄在 Office 互操作中非常臭名昭著,它们会导致保持 Office 程序运行的非常明显的副作用。

看到 RefCnt=0 并不罕见,也不一定表示泄漏。这只是意味着终结器线程还没有开始调用 RCW 终结器。这需要时间,终结器线程仅在垃圾收集之后运行。

严重依赖 COM 互操作的托管代码的一个常见问题是它根本不会产生足够的垃圾。所以垃圾收集器永远不会(或很少)启动,终结器线程也不会释放 COM 引用。副作用是程序可以使用大量非托管 COM 代码使用的非托管内存。最坏的情况会导致OOM。您可以通过使用分析器或 Perfmon.exe 查看 GC 性能计数器来诊断此问题。确保 gen#0 集合发生在健康的剪辑中。如果这没有发生,那么托管代码需要通过调用 GC.Collect() 来提供帮助。

一个并非完全不常见的问题是在尝试释放 COM 引用时终结器线程死锁。该程序看起来运行正常,但内存使用量迅速增加。您可以通过使用调试器查看终结器线程来诊断此问题,您会看到它进行 COM 互操作调用并且没有进行。这是由于违反了 STA 线程的合同造成的,它必须泵送消息循环。

于 2013-11-11T14:32:17.313 回答