2011-10-09 18 views
9

我有一个应用程序,经历从单词go缓慢的内存泄漏。我如何找到悬挂终结器队列的原因?

使用ANTS内存分析器我可以看到所有泄漏的内存都由终结器队列的GC根保存。

我怀疑可能发生的事情是终结器死锁,等待锁定变为可用状态。

我们的类都没有实现明确的终结器,我们通常会避免它们,这让我认为锁可能与系统或库类有关。

我用SOS.dll来看看终结队列的内容,如果我正确地解释它,然后它报告的第一个项目,如果队列的头实际上代表是一个实例System.Threading.Thread但是我不确定当前正在处理的对象或将要处理的下一个对象。

  • 是否有任何技巧可以用来找出最终确定的内容?
  • 有没有一种方法,我可以找出什么锁终结线程正在等待?
  • 是否可以打开任何额外的调试来跟踪终结器线程的操作?
  • 我还能看到什么?

更新

终结器线程的堆栈显示如下:

[email protected]() + 0x15 bytes 
[email protected]() + 0x15 bytes 
[email protected]() + 0x15 bytes  

[email protected]() + 0x43 bytes  
[email protected]() + 0x12 bytes 
ole32.dll!GetToSTA() + 0x72 bytes 

ole32.dll!CRpcChannelBuffer::SwitchAptAndDispatchCall() - 0x1939 bytes 
ole32.dll!CRpcChannelBuffer::SendReceive2() + 0xa6 bytes  
ole32.dll!CAptRpcChnl::SendReceive() + 0x5b7 bytes 
ole32.dll!CCtxComChnl::SendReceive() - 0x14b97 bytes  
ole32.dll!NdrExtpProxySendReceive() + 0x43 bytes  
[email protected]@4() + 0xe bytes  
rpcrt4.dll!_NdrClientCall2() + 0x144 bytes 
[email protected]() + 0x7a bytes  
[email protected]() + 0xf bytes 

ole32.dll!CObjectContext::InternalContextCallback() - 0x511f bytes 
ole32.dll!CObjectContext::ContextCallback() + 0x8f bytes  
clr.dll!CtxEntry::EnterContext() + 0x119 bytes 

clr.dll!RCWCleanupList::ReleaseRCWListInCorrectCtx() + 0x2bb bytes 

clr.dll!RCWCleanupList::CleanupAllWrappers() - 0x20fb0 bytes  
clr.dll!SyncBlockCache::CleanupSyncBlocks() + 0x1ec6 bytes 
clr.dll!Thread::DoExtraWorkForFinalizer() + 0x411b5 bytes 

clr.dll!WKS::GCHeap::FinalizerThreadWorker() + 0x8b bytes 
clr.dll!Thread::DoExtraWorkForFinalizer() + 0xb6e76 bytes 
clr.dll!Thread::ShouldChangeAbortToUnload() - 0x5f8 bytes 
clr.dll!Thread::ShouldChangeAbortToUnload() - 0x53d bytes 
clr.dll!ManagedThreadBase_NoADTransition() + 0x35 bytes  
clr.dll!ManagedThreadBase::FinalizerBase() + 0xf bytes 
clr.dll!WKS::GCHeap::FinalizerThreadStart() + 0xfb bytes  
clr.dll!Thread::intermediateThreadProc() + 0x48 bytes 
[email protected]@12() + 0x12 bytes  
[email protected]() + 0x27 bytes  
[email protected]() + 0x1b bytes  
+1

为什么你通常会避免使用终结器?正确执行一次性模式*需要*终结器。 – svick

+2

@svick - 关于MSDN的说明。实施IDisposable说:“如果一个班级本身不拥有非托管资源,则完全不使用终结器” – chillitom

+1

@svick - chillitom是正确的。引用[框架设计指南](http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613):“如果你能提供帮助,你真的不想写一个终结器。” – TrueWill

回答

9

在我看来您有一个COM服务器有问题。调用堆栈显示它正试图在单线程COM对象上进行IUnknown :: Release()调用。 ReleaseRCWListInCorrectCtx()调用将其设置为关闭,_NtUserPostMessage @ 16()是将请求整理到拥有该COM对象的STA的调用。

典型的原因是创建COM对象而不是抽取消息循环。 STA线程的硬性要求。您可以通过在主UI线程上创建它们并从不阻止它来避免它。

+1

spot,程序的主要方法被标记为[STAThread]以适应GUI模式,但通常作为Windows服务运行。最近一个COM组件必须被引入,可能是一个Timer。正如你所预测的,没有UI线程,服务的终结器就会挂起。 – chillitom

+1

仅供参考,如果您的主线程正在执行类似于“Console.ReadLine”的操作,但您希望它将信息抽取出来,则可以启动一个新的线程来执行readline,然后在Main上使用'.Join'线。在STA线程上调用内部'Thread.Join'时,将会泵送COM消息,这将使对象能够使用它。 –

相关问题