2009-03-27 27 views
9

? Estoy aprendiendo C#. Por lo que sé, tienes que configurar las cosas correctamente para que el recolector de basura realmente elimine todo como debería ser. Estoy buscando la sabiduría que aprendió durante años de ti, el inteligente.¿Cuáles son las formas de resolver las pérdidas de memoria en C#

Vengo de un fondo C++ y estoy MUY acostumbrado a los olores de código y patrones de desarrollo. Quiero aprender cómo son los olores de código en C#. ¡Dame un consejo!

¿Cuáles son las mejores formas de eliminar cosas?

¿Cómo puede saber cuándo tiene "pérdidas de memoria"?


Editar: Estoy tratando de desarrollar un punzón de lista de "cosas que hacer siempre para la gestión de la memoria"


Gracias, por lo tanto.

Respuesta

17

C#, .NET Framework usa la memoria administrada y todo (pero los recursos no administrados asignados) es basura recolectada.

Es seguro asumir que los tipos administrados siempre son basura recolectada. Eso incluye arrays, classes y structures. Siéntase libre de hacer int[] stuff = new int[32]; y olvidarse de eso.

Si abre un archivo, una conexión de base de datos o cualquier otro recurso no administrado en una clase, implemente la interfaz IDisposable y en su método Dispose, desasigne el recurso no administrado.

Cualquier clase que implemente IDisposable debe cerrarse explícitamente, o usarse en un bloque (me parece genial) Using;

using (StreamReader reader = new StreamReader("myfile.txt")) 
{ 
    ... your code here 
} 

Aquí .NET dispondrá el lector cuando esté fuera del {} alcance.

+1

Eso "o" es confuso - incluso objetos IDisposable son basura recogida. El GC no sabe nada sobre IDisposable. A menudo, Dispose() llama a SuppressFinalize, pero no está relacionado. –

+0

Buen punto. Entonces la clase contenedora es basura recolectada, pero sus recursos son explícitamente desasignados. Nunca pensé en eso. –

+3

"Es seguro asumir que los tipos administrados siempre son basura recolectada". Esta declaración es tan incorrecta. No se puede recolectar basura si todavía se puede alcanzar, el programador debe tener siempre en cuenta anular sus referencias raíz cuando ya no las necesite. –

0

¿Cuáles son las mejores formas de borrar cosas?

NOTA: las siguientes obras solamente para los tipos que contienen recursos no administrados. No ayuda con tipos puramente gestionados.

Probablemente el mejor método es implementar y seguir el patrón IDisposable; y llame al método de eliminación en todos los objetos que lo implementan.

La declaración 'using' es tu mejor amigo. En pocas palabras, llamará a deshacerse de ti sobre los objetos que implementen IDisposable.

11

Lo primero con GC es que no es determinista; si desea limpiar un recurso rápidamente, implemente IDisposable y use using; eso no recoge la memoria administrada, pero puede ayudar mucho con recursos no administrados y cadenas futuras.

En particular, las cosas a tener en cuenta:

  • un montón de fijar (coloca una gran cantidad de restricciones en lo que el GC puede hacer)
  • montón de finalizadores (que normalmente no los necesita; ralentiza GC)
  • eventos estáticos - manera fácil de mantener una gran cantidad de objetos grandes gráficos de vivos ;-P
  • eventos en un objeto de larga duración bajo costo, que puede ver un objeto caro que debería haber sido limpiado
  • " variables capturadas "accidentalmente manteniendo gráficos vivos

Para investigar fugas de memoria ..." SOS "es una de las rutas más fáciles; puede usar SOS para buscar todas las instancias de un tipo, y qué puede ver, etc.

+1

Amen a los eventos estáticos que son peligrosos. – Quibblesome

3

En general, cuanto menos se preocupe por la asignación de memoria en C#, mejor estará. Dejaría que un generador de perfiles me diga cuándo tengo problemas con la colección.

No puede crear pérdidas de memoria en C# de la misma manera que lo hace en C++. El recolector de basura siempre "tendrá tu espalda". Lo que puedes hacer es crear objetos y mantener referencias a ellos aunque nunca los uses. Ese es un olor codificado a tener en cuenta.

Aparte de eso:

  • Tener una idea de con qué frecuencia se producirá la colección (por razones de rendimiento)
  • No sostenga referencias a objetos más largas de las que necesita
  • Eliminar los objetos que implementan IDisposable tan pronto como haya terminado con ellos (utilizar la sintaxis using)
  • aplicar correctamente la IDisposable interface
0

Puede usar herramientas como CLR profiler, lleva un tiempo aprender a usarla correctamente, pero después de todo es gratis. (Me ayudó varias veces para encontrar mi pérdida de memoria)

2

Las principales fuentes de pérdidas de memoria que se me ocurren son:

  • mantenimiento de referencias a objetos que no necesitan más (por lo general en algún tipo de colección) Así que aquí debe recordar que todas las cosas que agregue a una colección que tenga referencia permanecerán en la memoria.

  • Tener referencias circulares, p. Ej. tener delegados registrados en un evento. Por lo tanto, aunque explícitamente no haga referencia a un objeto, no se puede obtener la basura recolectada porque uno de sus métodos está registrado como un delegado con un evento. En estos casos, debe recordar eliminar al delegado antes de descartar la referencia.

  • Interoperando con código nativo y no poder liberarlo. Incluso si usa wrappers gestionados que implementan finalizadores, a menudo el CLR no los limpia lo suficientemente rápido, porque no comprende la huella de memoria.Debe usar el patrón using (IDisposable) {}
+1

"referencias circulares" no son el problema real: el problema es que uno de los dos elementos todavía * puede * verse válidamente, y el evento mantiene vivo el segundo objeto. –

1

Otra cosa a considerar para la administración de la memoria es si está implementando cualquier patrón Observer y no elimina las referencias correctamente.

Por ejemplo: relojes de objetos un objeto B Objeto B está dispuesto si la referencia de A a B no está dispuesto de la propiedad de la GC no disponer properyly del objeto. Debido a que el controlador de eventos todavía está asignado, el GC no lo ve como un recurso no utilizado.

Si tiene un conjunto pequeño de objetos con los que está trabajando, me puede resultar irrelevante. Sin embargo, si trabaja con miles de objetos, esto puede ocasionar un aumento gradual en la memoria durante la vida útil de la aplicación.

Existen algunas excelentes aplicaciones de software de administración de memoria para monitorear lo que sucede con el montón de su aplicación. Encontré un gran beneficio al utilizar .Net Memory Profiler.

HTH

1

recomiendo el uso de .NET Memory Profiler

.NET memoria Profiler es una herramienta poderosa para encontrar fugas de memoria y optimizar el uso de memoria en los programas escritos en C#, VB.NET o cualquier otra .NET Idioma.

.NET memoria Profiler le ayudará a:

  • Ver memoria y recursos de información en tiempo real
  • identificar fácilmente las pérdidas de memoria mediante la recopilación y comparación de instantáneas de la memoria .NET
  • encontrar ejemplos que son no está correctamente dispuesto
  • Obtenga información detallada sobre el uso de recursos no administrados
  • Optimizar el uso de la memoria
  • Investigar problemas de memoria en el código de producción
  • Realizar la memoria de prueba automatizada de
  • recuperar información acerca de la memoria nativa

Tome un vistazo a sus tutoriales en vídeo:

http://memprofiler.com/tutorials/

0

La mejor manera de asegurar que los objetos se eliminen, o en .NET lingo, recogido de basura, es asegurar que todas las referencias de raíz (referencias que se pueden rastrear a través de métodos y objetos al primer método en una pila de llamadas del hilo) a un objeto se establece en nulo.

El GC no puede, y no recogerá, un objeto si hay referencias rooteadas, sin importar si implementa IDisposable o no.

Las referencias circulares no imponen ninguna penalización o posibilidad de pérdida de memoria, ya que el GC marca los objetos que ha visitado en el gráfico del objeto. En el caso de delegados o eventhandlers, puede ser común olvidarse de eliminar la referencia en un evento a un método de destino, de modo que el objeto que contiene el método de destino no se pueda recopilar si el evento está rooteado.

1

Otros ya han mencionado la importancia de IDisposable, y algunas de las cosas a tener en cuenta en su código.

Quería sugerir algunos recursos adicionales; Encontré lo siguiente invaluable cuando conozco los detalles de .NET GC y cómo solucionar problemas de memoria en aplicaciones .NET.

CLR via C# por Jeffrey Richter es un excelente libro. Vale la pena el precio de compra solo para el capítulo sobre GC y memoria.

Este blog (por un "Ingeniero de escalamiento de ASP.NET" de Microsoft) suele ser mi fuente de referencia para consejos y trucos para usar WinDbg, SOS y detectar ciertos tipos de pérdidas de memoria. Tess incluso diseñó demostraciones/laboratorios de depuración de .NET que lo guiarán a través de problemas comunes de memoria y cómo reconocerlos y resolverlos.

Debugging Tools for Windows (WinDbg, SOS, etc.)

+0

El blog de Tess está absolutamente recomendado para este tema, sí. –

Cuestiones relacionadas