9

I have an application that experiences a slow memory leak from the word go.

Using ANTS Memory Profiler I can see that all of the leaked memory is being held by the finalizer queue's GC root.

I suspect what may have happened is that the finalizer is deadlocked waiting on a lock to become available.

None of our classes implement explicit finalizers, we avoid them as a rule, this makes me think the lock might related to a system or library class.

I've used SOS.dll to take a look at the contents of the finalizer queue and if I am interpreting it correctly then it reports the first item to be an instance System.Threading.Thread However I am unsure if the head of the queue actually represents the currently being disposed object or the next object to be disposed.

  • Are there any tricks I can use to find out what is being finalized?
  • Is there a way I can find out what lock the finalizer thread is waiting on?
  • Can any extra debugging be turned on to trace the finalizer thread's actions?
  • What else can I look at?

Update

The finalizer thread's stack appears as follows:

ntdll.dll!_ZwWaitForSingleObject@12()  + 0x15 bytes  
ntdll.dll!_ZwWaitForSingleObject@12()  + 0x15 bytes  
user32.dll!_NtUserPostMessage@16()  + 0x15 bytes     

kernel32.dll!_WaitForSingleObjectExImplementation@12()  + 0x43 bytes     
kernel32.dll!_WaitForSingleObject@8()  + 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    
rpcrt4.dll!@NdrpProxySendReceive@4()  + 0xe bytes    
rpcrt4.dll!_NdrClientCall2()  + 0x144 bytes  
ole32.dll!_ObjectStublessClient@8()  + 0x7a bytes    
ole32.dll!_ObjectStubless@0()  + 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   
kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes     
ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes    
ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes     
casperOne
  • 73,706
  • 19
  • 184
  • 253
chillitom
  • 24,888
  • 17
  • 83
  • 118
  • 1
    Why are you avoiding finalizers as a rule? Proper implementation of the disposable pattern *requires* finalizer. – svick Oct 09 '11 at 16:20
  • 2
    @svick - A note on MSDN re. implementing IDisposable says "Leave out the finalizer altogether if a class doesn't own unmanaged resources itself" – chillitom Oct 09 '11 at 16:27
  • 1
    @svick - chillitom is correct. Quoting [Framework Design Guidelines](http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613): "You really don’t want to write a finalizer if you can help it." – TrueWill Oct 09 '11 at 17:01
  • 1
    @svick I can't think of many situations that require a classical finalizer. You usually use a SafeHandle for unmanaged resources, and managed resources don't need a finalizer. – CodesInChaos Oct 09 '11 at 17:04
  • To be absolutely clear then, you are avoiding _destructors_ . They are part of the _implicit_ finaliztion pattern. Dispose() is the explicit form. – H H Oct 09 '11 at 17:39
  • @HenkHolterman - yes, we make use of IDisposable frequently but haven't yet had need to implement destructors on our classes as we don't own any unmanaged resources. – chillitom Oct 10 '11 at 07:58

1 Answers1

11

Looks to me you are having a problem with a COM server. The call stack shows it is trying to make the IUnknown::Release() call on a single-threaded COM object. The ReleaseRCWListInCorrectCtx() call sets it off, the _NtUserPostMessage@16() is the call that marshals the request to the STA that owns the COM object.

The typical cause is creating COM objects and not pumping a message loop. A hard requirement for STA threads. You avoid it by creating them on the main UI thread and never blocking it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    spot on, the program's main method was marked [STAThread] to accommodate a GUI mode but would normally run as a windows service. Recently a COM component must have been introduced, likely candidate a Timer. As you predicted with no UI thread the service's finalizer just hangs. – chillitom Oct 10 '11 at 13:51
  • 1
    FYI, If your main thread is doing something like `Console.ReadLine`, but you want it to be pumping messages, you can spin up a new Thread to do the readline, and then use `.Join` on it from your Main thread. Internally `Thread.Join` when called on an STA thread will pump COM messages, which should enable your objects to use it. – Orion Edwards Nov 28 '12 at 23:51