我有一个非常广泛使用托管和非托管对象的应用程序。 有内存泄漏,我试图了解如何解决他们的问题。我使用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)
答案 0 :(得分:2)
它们是垃圾收集句柄。在.NET Framework中使用GCHandle类型公开。它们保留了对托管对象的引用,超出了GC可以自己发现的正常对象。
有几种不同的用途,用于不同的目的。 GCHandleType enum显示一些,弱引用和固定是常见用途。 GCHandleType.Normal在非托管互操作中很常见,它允许非托管代码保持对托管对象的引用,gcroot<>
是它的包装。
它们也被CLR使用,GCHandleType未公开的类型。使用“RefCnt”在SOS输出中显示的是一种用于COM互操作的特殊类型,它保留对RCW,Runtime Callable Wrapper,COM接口的托管包装器对象的引用。这种句柄在Office互操作中非常臭名昭着,它们会导致保持Office程序运行的非常明显的副作用。
看到RefCnt = 0并不罕见,并不一定表示泄漏。它只是意味着终结器线程尚未调用RCW终结器。这需要时间,终结器线程只在垃圾收集后运行。
严重依赖COM互操作的托管代码的一个常见问题是它根本不会产生足够的垃圾。因此,垃圾收集器永远(或很少)启动并且终结器线程无法释放COM引用。副作用是程序可以使用大量非托管内存,由非托管COM代码使用。最坏的情况是它会导致OOM。您可以通过使用分析器或Perfmon.exe查看GC性能计数器来诊断此问题。确保gen#0集合正在健康剪辑中发生。如果没有发生,则托管代码需要通过调用GC.Collect()来帮助。
一个并不完全不常见的问题是终结器线程在尝试释放COM引用时死锁。该程序看起来运行正常,但内存使用量迅速增加。你通过使用调试器查看终结器线程来诊断它,你会看到它进行COM互操作而不是进展。这是因为违反了STA线程的合同,它必须引发一个消息循环。