.net optimization memory-leaks

Fugas de memoria en.NET



optimization memory-leaks (14)

¿Cuáles son todas las formas posibles en que podemos obtener filtraciones de memoria en .NET?

Sé de dos:

  1. No registrar correctamente a los Manejadores de eventos / Delegados .
  2. No descarta controles dinámicos secundarios en Windows Forms:

Ejemplo:

// Causes Leaks Label label = new Label(); this.Controls.Add(label); this.Controls.Remove(label); // Correct Code Label label = new Label(); this.Controls.Add(label); this.Controls.Remove(label); label.Dispose();

Actualización : La idea es enumerar riesgos comunes que no son demasiado obvios (como el anterior). Por lo general, la noción es que las pérdidas de memoria no son un gran problema debido al recolector de basura. No como solía ser en C ++.

Gran discusión chicos, pero déjenme aclarar ... por definición, si no queda referencia para un objeto en .NET, será basura recogida en algún momento. Entonces esa no es una forma de inducir fugas de memoria.

En el entorno administrado, lo consideraría una pérdida de memoria si tuviera una referencia involuntaria a algún objeto del que no tenga conocimiento (de ahí los dos ejemplos en mi pregunta).

Entonces, ¿cuáles son las diversas formas posibles en que puede ocurrir esa pérdida de memoria?


  1. Mantener referencias a los objetos que ya no necesita.

En otros comentarios: una forma de garantizar que Dispose se llame es usar usando ... cuando la estructura del código lo permita.


¿Estás hablando de uso de memoria inesperado o fugas reales? Los dos casos que enumeró no son exactamente fugas; son casos en que los objetos se quedan más tiempo de lo previsto.

En otras palabras, son referencias a las que la persona que las llama filtraciones de memoria no sabía o no había olvidado.

Editar: O son errores reales en el recolector de basura o el código no administrado.

Edición 2: Otra forma de pensar sobre esto es asegurarse siempre de que las referencias externas a sus objetos se publiquen adecuadamente. Código de medios externos fuera de su control. Cualquier caso en que eso ocurra es un caso en el que puede "perderse" la memoria.



Eso realmente no causa fugas, solo hace más trabajo para el GC:

// slows GC Label label = new Label(); this.Controls.Add(label); this.Controls.Remove(label); // better Label label = new Label(); this.Controls.Add(label); this.Controls.Remove(label); label.Dispose(); // best using( Label label = new Label() ) { this.Controls.Add(label); this.Controls.Remove(label); }

Dejar los componentes desechables por ahí nunca es un gran problema en un entorno administrado como .Net: esa es una gran parte de lo que significa administrar.

Acelerarás tu aplicación, sin duda. Pero no dejarás un lío por nada más.


Estableciendo la propiedad GridControl.DataSource directamente sin usar una instancia de la clase BindingSource ( http://msdn.microsoft.com/en-us/library/system.windows.forms.bindingsource.aspx ).

Esto causó filtraciones en mi aplicación que me llevó bastante tiempo rastrear con un generador de perfiles, finalmente encontré este informe de errores al que respondió Microsoft: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=92260

Es curioso que en la documentación de la clase BindingSource Microsoft trate de pasarlo como una clase legítima y bien pensada, pero creo que lo crearon para resolver una filtración fundamental relacionada con los administradores de divisas y datos vinculantes para los controles de la red.

¡Cuidado con este, apuesto a que hay un montón de aplicaciones con fugas por ahí!


Excepciones en los métodos Finalizar (o Descartar llamadas desde un Finalizador) que impiden que los recursos no administrados se eliminen correctamente. Uno común es debido a que el programador asume qué orden se eliminarán los objetos e intenta liberar los objetos pares que ya se han eliminado, lo que da como resultado una excepción y no se llama al resto del método Finalizar / Eliminar de Finalizar.


Llamar a IDisposable cada vez es el lugar más fácil para comenzar, y definitivamente una forma efectiva de obtener toda la fruta de fuga de memoria que cuelga en la base del código. Sin embargo, no siempre es suficiente. Por ejemplo, también es importante comprender cómo y cuándo se genera el código administrado en el tiempo de ejecución, y que una vez que los ensamblajes se cargan en el dominio de la aplicación, nunca se descargan, lo que puede aumentar la huella de la aplicación.


Los hilos interbloqueos nunca liberarán raíces. Obviamente, podrías argumentar que el punto muerto presenta un problema mayor.

Un subproceso de finalizador interbloqueado evitará que se ejecuten todos los finalizadores restantes y, por lo tanto, evitará que se recuperen todos los objetos finalizables (ya que todavía están siendo rooteados por la lista de posibilidades).

En una máquina con varias CPU, podría crear objetos finalizables más rápido de lo que el finalizador podría ejecutar finalizadores. Mientras eso sea sostenido, "perderás" la memoria. Probablemente no sea muy probable que esto suceda en la naturaleza, pero es fácil de reproducir.

El montón de objetos grandes no está compactado, por lo que podría perder memoria a través de la fragmentación.

Hay una serie de objetos que deben liberarse manualmente. Ej. Objetos remotos sin arrendamiento y ensambles (debe descargar AppDomain).


Muchas de las cosas que pueden causar pérdidas de memoria en idiomas no administrados aún pueden causar pérdidas de memoria en los idiomas administrados. Por ejemplo, las malas políticas de almacenamiento en caché pueden provocar pérdidas de memoria.

Pero como Greg y Danny dijeron, no hay una lista completa. Cualquier cosa que pueda dar como resultado la conservación de la memoria después de su vida útil puede causar una fuga.


No hay forma de proporcionar una lista completa ... esto es muy parecido a preguntar "¿Cómo puedes mojarte?"

Dicho esto, asegúrese de estar llamando a Dispose () en todo lo que implementa IDisposable, y asegúrese de implementar IDisposable en cualquier tipo que consuma recursos no administrados de ningún tipo.

De vez en cuando, ejecuta algo como FxCop en tu base de código para ayudarte a hacer cumplir esa regla: te sorprendería la profundidad con la que algunos objetos desechables quedan enterrados dentro de un marco de aplicación.


Para evitar fugas de memoria .NET:

1) Emplee la construcción ''using'' (o ''try'') cuando se cree un objeto con la interfaz ''IDisposable''.

2) Hacer clases ''IDisposable'' si crean un hilo o agregan un objeto a una colección estática o de larga vida. Recuerde que C # ''evento'' es una colección.

Aquí hay un breve artículo sobre consejos para evitar fugas de memoria .


Tengo 4 elementos adicionales para agregar a esta discusión:

  1. Terminar subprocesos (Thread.Abort ()) que han creado los controles de UI sin prepararse adecuadamente para un evento de este tipo puede hacer que la memoria se utilice de forma expectante.

  2. Acceder a recursos no administrados a través de Pinvoke y no limpiarlos puede provocar pérdidas de memoria.

  3. Modificar objetos de cadena grandes. No necesariamente una pérdida de memoria, una vez fuera del alcance, GC se encargará de eso, sin embargo, en cuanto al rendimiento, su sistema puede tener un impacto si las cadenas grandes se modifican a menudo porque realmente no puede depender de GC para garantizar que la huella de su programa mínimo.

  4. Creación de objetos GDI a menudo para realizar dibujos personalizados. Si realiza GDI a menudo, reutilice un solo objeto gdi.


Tess Fernandez Tiene excelentes publicaciones en el blog sobre cómo encontrar y depurar fugas de memoria. Lab 6 Lab 7


Una cosa que fue realmente inesperada para mí es esta:

Region oldClip = graphics.Clip; using (Region newClip = new Region(...)) { graphics.Clip = newClip; // draw something graphics.Clip = oldClip; }

¿Dónde está la fuga de memoria? ¡Bien, deberías haber oldClip también! Porque Graphics.Clip es una de las pocas propiedades que devuelve un nuevo objeto desechable cada vez que se invoca el captador.