2008-12-05 7 views
6

Tengo un servicio que tiene una fuga de memoria muy lenta. Si analizo los contadores de carga de .NET CLR, veo que el contador Current Classes Loaded está en constante aumento y coincide con el contador Total Classes Loaded en todo momento. Esto me da la impresión de que la pérdida de memoria está relacionada con recursos que no se liberan (esto es solo una suposición).Clases actuales cargadas constantemente en aumento: pérdida de memoria

El servicio crea nuevos appDomains cada vez que realiza una tarea (arquitectura de plug-in).

Necesito averiguar los nombres de clase para poder reducir la causa de la fuga. No soy muy hábil con WinDbg, pero me preguntaba si alguien podría guiarme por los pasos para enumerar estas clases cargadas.

Tengo el código fuente así que puedo obtener los archivos de símbolos si es necesario. ¡Gracias de antemano por cualquier ayuda!

+1

¿Estás destruyendo el nuevo AppDomain cada vez que lo usas? –

+0

Sí, se llama AppDomain.Unload. Además, los contadores de rendimiento para AppDomains no aumentan constantemente. –

Respuesta

2

Creo que el problema se debió en realidad a una serie de instancias de FileSystemWatcher no dispuestas anotadas en el MBT de RemoteTaskRunner. Todavía no estoy seguro de haber resuelto por completo la fuga de memoria, pero definitivamente puedo notar la diferencia.

Parece que esta no es la primera vez que los FileSystemWatchers me causan problemas. :)

¡Gracias a todos (especialmente a los leppies) por ayudarme con esto!

1

Sugeriría utilizar un analizador de memoria adecuado - como ".NET Memory Profiler" - http://memprofiler.com/ Sin duda puede probarlo en la evaluación para ver si es el tipo de herramienta que ayudará.

Eso le permitirá ver todos los objetos en vivo mucho más fácilmente que las cosas duras de WinDBG/SoS.

0

EDITAR: Después de leer algunas de las otras publicaciones, se deben tener en cuenta después de usar un mejor generador de perfiles y después de resolver el problema del dominio de la aplicación.

Es posible que desee agregar contadores de rendimiento en su servicio para poder hacer un seguimiento de al menos los objetos que crea. Eso te ayudará a determinar si Classes.Loaded es tuyo o clases de CLR.

También puede ser útil agregar algunos registros de depuración para cuando cree y destruya explícitamente sus objetos.

Como último recurso, puede probar poniendo un GC.Collect() allí para ver si al llamarlo se corrige su problema. No es el arreglo, pero probarlo le permitirá saber que es una opción.

2

¿Es este .net 2.0 o superior? Si es así, es posible que no descargues el AppDomain (como dice Jon Skeet en el comentario).

Si es 1.1 o inferior, hay un error en la funcionalidad de descarga AppDomain. Es decir. no libera la memoria ni libera recursos cuando un AppDomain está "descargado".

(Esto se fijó como de .NET 2.0)

+0

Acepto, deben descargarse. – leppie

1

Según lo dicho por la otra respuesta, AppDomain.Unload() se debe utilizar.

Sin embargo, debe tener cuidado de no cargar ensamblajes en varios lugares, especialmente en el dominio de aplicación principal.

Mire los documentos de MSDN para AppDomain.Load (AssemblyName) para obtener una explicación de cómo sucede lo anterior.

También en la misma línea, ¿está seguro de que está utilizando clases remotas adecuadas? Si no, lo anterior seguramente sucederá.

0

Los AppDomains están descargados, pero la respuesta de leppie hace que me pregunte si los plugin-assemblies se están cargando tanto en el AppDomain primario como en el AppDomain secundario. Cuando miro los contadores de rendimiento, el recuento actual de AppDomain no aumenta constantemente.

Se supone que la aplicación debe crear unDominio de aplicación secundario y luego cargar un ensamblaje de complemento por separado. Tal vez algo de código ayudaría:

Creación del dominio de aplicación secundaria del dominio de aplicación principal:

AppDomainSetup ads = new AppDomainSetup(); 
ads.ApplicationName = "RemoteAgentLib"; 
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; 
ads.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory; 
ads.ShadowCopyDirectories = AppDomain.CurrentDomain.BaseDirectory; 
ads.ShadowCopyFiles = "true"; 

m_domain = AppDomain.CreateDomain("RemoteTaskRunner", null, ads); 

Utilice la RemoteTaskRunner para cargar el plug-in en el dominio de aplicación secundaria:

RemoteTaskRunner taskRunner = m_domain.CreateInstanceAndUnwrap(
        Assembly.GetExecutingAssembly().FullName, 
        typeof (RemoteTaskRunner).FullName) as RemoteTaskRunner; 
taskRunner.LoadTask(taskInfo.Assembly, taskInfo.Type); 

Uso del RemoteTaskRunner a Ejecutar la tarea en el appDomain secundario:

[Serializable] 
internal class RemoteTaskRunner : MarshalByRefObject 
{ 
    private ITask m_task; 

    public RemoteTaskRunner() 
    { 
    } 

    internal void LoadTask(string assembly, string type) 
    { 
     // This assembly should load in the secondary appDomain. 
     Assembly taskAssembly = AppDomain.CurrentDomain.Load(assembly); 
     m_task = taskAssembly.CreateInstance(type) as ITask; 
    } 

    internal void RunTask(string taskConfig) 
    { 
     // This method should run in the secondary appDomain. 
     m_task.RunTask(taskConfig, m_logger); 
    } 
... 
... 

Para ejecutar la tarea plugin, la siguiente línea de código se utiliza en el dominio de aplicación principal:

taskRunner.RunTask(taskInfo.TaskConfig); 

Después de que finalice la tarea, el dominio de aplicación se descarga:

AppDomain.Unload(m_domain); 
+0

Puede simplemente volcar todos los ensamblajes cargados en el dominio principal y ver si se cargan incorrectamente de alguna manera. Esto puede ser muy complicado. – leppie

+0

Por cierto, ¿debería MarshalByRefObject ser serializable? ¿Puedes verificar, después de crearlo, que de hecho es un proxy transparente? – leppie

+0

El atributo Serializable no parece tener un efecto en la ejecución. Tanto con como sin RemoteTaskRunner es un proxy transparente. –

1

simplemente leí esto en el blog de Suzanne Cook.

http://blogs.msdn.com/suzcook/archive/2003/06/12/57169.aspx

Asegúrese de no pasar cualquier Tipo/Asamblea/etc. instancias (además de su tipo MarshalByRefObject) vuelva a el dominio de aplicación original. Si lo hace, hará que esos ensambles sean cargados en el dominio de aplicación original. Si la configuración del dominio de aplicación es diferente entre los dos dominios de aplicación, esos ensamblajes pueden no ser cargados allí. Además, incluso si se cargan con éxito , los ensamblajes permanecerán cargados y bloqueados después de que se descargue el dominio de aplicación , incluso si el dominio de aplicación original nunca los usa.

Cuando dice cualquier Tipo/Ensamblaje/etc. ¿Qué puede hacer ALGÚN tipo? El motivo por el que pregunto es porque mi MarshalByRefObject (RemoteTaskRunner) devuelve un objeto DateTime después de que se ejecuta la tarea. ¿Podría esto provocar que el ensamblaje del complemento se cargue en mi appDomain principal (y en última instancia cause la pérdida de memoria)?

+0

No, un DateTime está bien, ya que el ensamblado mscorlib ya está cargado. – leppie

2

Siempre se puede comprobar lo asambleas se cargan en su dominio de aplicación:

foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
{ 
     Console.WriteLine(assembly.FullName); 
} 

Así que si accidentalmente se cargan los ensamblados en el dominio incorrecto que no será demasiado duro ver.

Editar:

si desea utilizar WinDgb SOS, here son los comandos admitidos. Lo más probable es que le interesen: DumpDomain, DumpClass, DumpAssembly, EEHeap ...

1

Siempre arrojo esto, cada vez que alguien informa una pérdida de memoria, porque me mantuvo ocupado durante un par de semanas. No ejecute su aplicación en modo de depuración. Si ejecuta su aplicación en modo de depuración en .NET 2.0+ (no sucedió en .Net 1.1), y crea una instancia de una clase que contiene un evento, incluso si no lo plantea, solo tendrá una pequeña pedazo de memoria. Esto puede afectar en gran medida las aplicaciones de larga ejecución, como los servicios y las aplicaciones web, ya que con el tiempo la pequeña cantidad de memoria consumida después de la instancia de los objetos puede sumar mucho.

Cuestiones relacionadas