2011-10-09 22 views
9

Tengo una aplicación que experimenta una fuga de memoria lenta desde el principio.¿Cómo puedo encontrar el motivo de una cola colgada del finalizador?

Usando ANTS Memory Profiler Veo que toda la memoria filtrada está siendo retenida por la raíz de la cola del finalizador de la GC.

sospecho lo que puede haber sucedido es que el finalizador está en punto muerto a la espera de una cerradura que estén disponibles.

Ninguna de nuestras clases implementan finalizadores explícitos, los evitamos, por regla general, esto me hace pensar el poder de bloqueo en relación con un sistema o una clase de biblioteca.

He usado SOS.dll para ver el contenido de la cola del finalizador y si lo interpreto correctamente, entonces informa que el primer elemento es una instancia System.Threading.Thread Sin embargo, no estoy seguro de si el encabezado de la cola realmente representa el objeto actualmente dispuesto o el siguiente objeto a eliminar.

  • ¿Hay algún truco que pueda usar para averiguar qué se está finalizando?
  • ¿Hay alguna manera de averiguar qué bloqueo está esperando el finalizador?
  • ¿Se puede activar alguna depuración adicional para rastrear las acciones del thread del finalizador?
  • ¿Qué más puedo ver?

actualización

pila del subproceso finalizador aparece como sigue:

[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

¿Por qué se evitan los finalizadores como regla? La implementación correcta del patrón desechable * requiere * finalizador. – svick

+2

@svick - Una nota en MSDN re. la implementación de IDisposable dice "Deje el finalizador si una clase no posee recursos no administrados" – chillitom

+1

@svick - chillitom es correcto. Citando [Framework Design Guidelines] (http://www.amazon.com/Framework-Design-Guidelines-Conventions-Libraries/dp/0321545613): "Realmente no desea escribir un finalizador si puede evitarlo". – TrueWill

Respuesta

9

A mi me parece que está teniendo un problema con un servidor COM. La pila de llamadas muestra que está intentando realizar la llamada IUnknown :: Release() en un objeto COM de subproceso único. La llamada ReleaseRCWListInCorrectCtx() lo establece, _NtUserPostMessage @ 16() es la llamada que dirige la solicitud al STA que posee el objeto COM.

La causa típica es crear objetos COM y no bombear un bucle de mensajes. Un requisito difícil para los hilos STA. Lo evitas al crearlos en el hilo principal de UI y nunca bloquearlo.

+1

, el método principal del programa fue marcado [STAThread] para acomodar un modo de GUI pero normalmente se ejecutaría como un servicio de Windows. Recientemente se debe haber introducido un componente COM, probablemente candidato a Timer. Como predijo sin ningún subproceso de interfaz de usuario, el finalizador del servicio simplemente se bloquea. – chillitom

+1

FYI, si su hilo principal está haciendo algo como 'Console.ReadLine', pero quiere que esté bombeando mensajes, puede hacer girar un nuevo hilo para hacer la línea de lectura, y luego usar' .Join' en él desde su Main hilo. Internamente 'Thread.Join' cuando se llama en un hilo STA bombeará mensajes COM, lo que debería permitir que sus objetos lo usen. –

Cuestiones relacionadas