c# - ¿Qué estrategias y herramientas son útiles para encontrar fugas de memoria en.NET?
memory-management memory-leaks (14)
¿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 fugas de memoria 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 programa. Esto parece ser una fuga, pero en realidad es solo un síntoma de la forma en que se asigna la memoria en el programa.
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 declaración de using que le permite crear un alcance limitado para la duración de un recurso.
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 fugas de memoria en .NET?
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.
Desde Visual Studio 2015, considere utilizar la herramienta de diagnóstico Uso de la memoria para recopilar 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.
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 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
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 aumentar 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.
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.
Prefiero dotmemory de Jetbrains
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.
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
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)
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 de proceso, los subprocesos y las pilas 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 bloqueo o volcar el proceso.
Luego analiza fuera de línea.
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.
Este blog tiene algunos recorridos realmente maravillosos que usan windbg y otras herramientas para rastrear fugas de memoria de todo tipo. Excelente lectura para desarrollar tus habilidades.
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.