¿Qué estrategias y herramientas son útiles para encontrar memory leaks en .NET?

Escribí C ++ por 10 años. Me encontré con problemas de memoria, pero podrían solucionarse con una cantidad razonable de esfuerzo.

Durante los últimos años he estado escribiendo C #. Me parece que todavía tengo muchos problemas de memoria. Son difíciles de diagnosticar y corregir debido a la falta de determinación y porque la filosofía de C # es que no debes preocuparte por esas cosas cuando definitivamente lo haces.

Un problema particular que encuentro es que tengo que eliminar y limpiar todo en el código explícitamente. Si no lo hago, entonces los perfiladores de memoria realmente no ayudan porque hay tanto desperdicio que no se puede encontrar una filtración dentro de todos los datos que están tratando de mostrar. Me pregunto si tengo una idea equivocada o si la herramienta que tengo no es la mejor.

¿Qué tipo de estrategias y herramientas son útiles para abordar memory leaks en .NET?

Uso MemProfiler de Scitech cuando sospecho que hay una pérdida de memoria.

Hasta ahora, he encontrado que es muy confiable y poderoso. Me ha salvado el tocino en al menos una ocasión.

El GC funciona muy bien en .NET IMO, pero al igual que cualquier otro idioma o plataforma, si escribe un código incorrecto, suceden cosas malas.

Solo por el problema del olvido para deshacerse, pruebe la solución que se describe en esta publicación de blog . Aquí está la esencia:

public void Dispose () { // Dispose logic here ... // It's a bad error if someone forgets to call Dispose, // so in Debug builds, we put a finalizer in to detect // the error. If Dispose is called, we suppress the // finalizer. #if DEBUG GC.SuppressFinalize(this); #endif } #if DEBUG ~TimedLock() { // If this finalizer runs, someone somewhere failed to // call Dispose, which means we've failed to leave // a monitor! System.Diagnostics.Debug.Fail("Undisposed lock"); } #endif 

Hemos utilizado Ants Profiler Pro por el software Red Gate en nuestro proyecto. Funciona muy bien para todas las aplicaciones basadas en el lenguaje .NET.

Descubrimos que .NET Garbage Collector es muy “seguro” en su limpieza de objetos en memoria (como debería ser). Mantendría los objetos cerca simplemente porque podríamos usarlo alguna vez en el futuro. Esto significaba que necesitábamos ser más cuidadosos con la cantidad de objetos que inflados en la memoria. Al final, convertimos todos nuestros objetos de datos a un “inflado a pedido” (justo antes de que se solicite un campo) para reducir la sobrecarga de memoria y boost el rendimiento.

EDITAR: Aquí hay una explicación más detallada de lo que quiero decir con “inflar según demanda”. En nuestro modelo de objetos de nuestra base de datos, usamos las Propiedades de un objeto principal para exponer los objetos secundarios. Por ejemplo, si tuviéramos algún registro que hiciera referencia a algún otro registro de “detalle” o “búsqueda” en una base uno a uno, lo estructuraríamos así:

 class ParentObject Private mRelatedObject as New CRelatedObject public Readonly property RelatedObject() as CRelatedObject get mRelatedObject.getWithID(RelatedObjectID) return mRelatedObject end get end property End class 

Descubrimos que el sistema anterior creaba algunos problemas reales de memoria y rendimiento cuando había muchos registros en la memoria. Así que cambiamos a un sistema donde los objetos se inflaban solo cuando se solicitaban, y las llamadas a la base de datos se realizaban solo cuando era necesario:

 class ParentObject Private mRelatedObject as CRelatedObject Public ReadOnly Property RelatedObject() as CRelatedObject Get If mRelatedObject is Nothing mRelatedObject = New CRelatedObject End If If mRelatedObject.isEmptyObject mRelatedObject.getWithID(RelatedObjectID) End If return mRelatedObject end get end Property end class 

Esto resultó ser mucho más eficiente porque los objetos se mantuvieron fuera de la memoria hasta que se necesitaron (se accedió al método Get). Proporcionó un aumento de rendimiento muy grande en la limitación de visitas a la base de datos y una gran ganancia en espacio de memoria.

Todavía debe preocuparse por la memoria cuando está escribiendo código administrado a menos que su aplicación sea trivial. Sugeriré dos cosas: primero, lea CLR a través de C # porque lo ayudará a comprender la gestión de memoria en .NET. En segundo lugar, aprenda a usar una herramienta como CLRProfiler (Microsoft). Esto puede darle una idea de lo que está causando su pérdida de memoria (por ejemplo, puede echar un vistazo a la fragmentación de gran montón de objetos)

¿Estás usando código no administrado? Si no está usando código no administrado, según Microsoft, las pérdidas de memoria en el sentido tradicional no son posibles.

Sin embargo, la memoria utilizada por una aplicación no se puede liberar, por lo que la asignación de memoria de una aplicación puede crecer durante la vida útil de la aplicación.

De Cómo identificar memory leaks en el tiempo de ejecución de lenguaje común en Microsoft.com

Se puede producir una pérdida de memoria en una aplicación .NET Framework cuando utiliza código no administrado como parte de la aplicación. Este código no administrado puede perder memoria, y el tiempo de ejecución de .NET Framework no puede resolver ese problema.

Además, un proyecto puede parecer tener una pérdida de memoria. Esta condición puede ocurrir si se declaran muchos objetos grandes (como objetos DataTable) y luego se agregan a una colección (como un DataSet). Los recursos que poseen estos objetos nunca se pueden liberar, y los recursos se dejan vivos durante toda la ejecución del progtwig. Esto parece ser una fuga, pero en realidad es solo un síntoma de la forma en que se asigna la memoria en el progtwig.

Para tratar este tipo de problema, puede implementar IDisposable . Si quieres ver algunas de las estrategias para lidiar con la administración de la memoria, te sugiero que busques la administración de memoria identificable, XNA, ya que los desarrolladores de juegos necesitan tener una recolección de basura más predecible y deben forzar al GC a hacer su trabajo.

Un error común es no eliminar los controladores de eventos que se suscriben a un objeto. Una suscripción a manejador de eventos evitará que un objeto sea reciclado. Además, eche un vistazo a la statement de uso que le permite crear un scope limitado para la duración de un recurso.

Este blog tiene algunos recorridos realmente maravillosos que usan windbg y otras herramientas para rastrear memory leaks de todo tipo. Excelente lectura para desarrollar tus habilidades.

Acabo de tener una pérdida de memoria en un servicio de Windows, que arreglé.

Primero, probé MemProfiler . Me pareció muy difícil de usar y nada amigable para el usuario.

Luego, utilicé JustTrace, que es más fácil de usar y le da más detalles sobre los objetos que no se eliminan correctamente.

Me permitió resolver la fuga de memoria muy fácilmente.

Si las filtraciones que está observando se deben a una implementación de caché desbocada, este es un escenario en el que es posible que desee considerar el uso de WeakReference. Esto podría ayudar a garantizar que la memoria se libera cuando sea necesario.

Sin embargo, en mi humilde opinión, sería mejor considerar una solución a medida, solo usted realmente sabe cuánto tiempo necesita para mantener los objetos alrededor, por lo que diseñar el código de limpieza adecuado para su situación suele ser el mejor enfoque.

Lo mejor a tener en cuenta es hacer un seguimiento de las referencias a sus objetos. Es muy fácil terminar colgando referencias a objetos que ya no te importan. Si no vas a usar algo más, deshazte de él.

Acostúmbrese a usar un proveedor de caché con caducidad de vencimientos, de modo que si no se hace referencia a algo para una ventana de tiempo deseada, se desreferencia y se limpia. Pero si se accede mucho, dirá en memoria.

Una de las mejores herramientas es usar las herramientas de depuración para Windows y tomar un volcado de memoria del proceso usando adplus , luego usar windbg y el complemento sos para analizar la memoria del proceso, los subprocesos y las stacks de llamadas.

También puede usar este método para identificar problemas en los servidores, después de instalar las herramientas, compartir el directorio, luego conectarse al recurso compartido desde el servidor usando (uso neto) y realizar un locking o volcar el proceso.

Luego analiza fuera de línea.

Big Guns – Herramientas de depuración para Windows

Esta es una increíble colección de herramientas. Puede analizar montones administrados y no administrados con él y puede hacerlo sin conexión. Esto fue muy útil para depurar una de nuestras aplicaciones ASP.NET que siguió reciclando debido al uso excesivo de memoria. Solo tuve que crear un volcado de memoria completa del proceso vivo que se ejecutaba en el servidor de producción; todo el análisis se realizó sin conexión en WinDbg. (Resultó que algún desarrollador estaba usando en exceso el almacenamiento de la sesión en memoria).

“Si está roto, es …” blog tiene artículos muy útiles sobre el tema.

Después de una de mis correcciones para la aplicación administrada, tuve la misma idea, por ejemplo, cómo verificar que mi aplicación no tenga la misma pérdida de memoria después del siguiente cambio, así que escribí algo así como el marco Verificación de la versión del objeto, eche un vistazo a el paquete NuGet ObjectReleaseVerification . Puede encontrar un ejemplo aquí https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample , e información sobre este ejemplo http://outcoldman.ru/en/blog/show/322

En Visual Studio (ediciones Premium o Enterprise), puede abrir el volcado del montón y depurar el montón administrado. Al hacer clic en un tipo, las tabs a continuación le permitirán ver qué otros tipos hacen referencia a esos objetos.

Puede usar PerfView para un análisis más detallado:

  1. En el menú Memoria, seleccione Tomar instantánea de montón.

  2. En el cuadro de diálogo resultante, seleccione el proceso para analizar y haga clic en Dump GC Heap. Opcionalmente, puede congelar el proceso o forzar la aparición de un GC antes de recostackr la instantánea.

  3. Una vez que finalice la captura, haga clic en Cerrar.

Visual Studio y PerfView son principalmente útiles para el análisis agregado. PerfView es un generador de perfiles de muestreo, incluso cuando analiza el montón, por lo que a veces dará una imagen sesgada de cómo se ve el montón. Si necesita profundizar en un objeto específico u obtener la verdad absoluta sobre la imagen completa, entonces debe comenzar a usar el depurador o CLR MD.

Prefiero dotmemory de Jetbrains

Desde Visual Studio 2015, considere utilizar la herramienta de diagnóstico Uso de la memoria para recostackr y analizar datos de uso de la memoria.

La herramienta Uso de memoria le permite tomar una o más instantáneas del montón de memoria administrada y nativa para ayudar a comprender el impacto en el uso de memoria de los tipos de objetos.