¿Qué estrategias y herramientas son útiles para encontrar pérdidas de memoria en .NET?

152

Escribí C ++ por 10 años. Encontré problemas de memoria, pero podrían solucionarse con un esfuerzo razonable.

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 debería tener que preocuparse por esas cosas cuando definitivamente lo hace.

Un problema particular que encuentro es que tengo que disponer y limpiar explícitamente todo en el código. Si no lo hago, entonces los perfiladores de memoria realmente no ayudan porque hay tanta paja flotando sobre ti que no puedes encontrar una fuga en todos los datos que intentan mostrarte. 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 las pérdidas de memoria en .NET?

Scott Langham
fuente
El título de su publicación realmente no coincide con la pregunta en su publicación. Te sugiero que actualices tu título.
Kevin
Tienes razón. Lo siento, me estaba hartando de la fuga actual que estoy buscando. Título actualizado.
Scott Langham
3
@Scott: No te hartes de .NET, no es el problema. Tu código es.
GEOCHET
3
Sí, mi código o las bibliotecas de terceros que tengo el placer de usar.
Scott Langham
@ Scott: Mira mi respuesta. MemProfiler lo vale. Usarlo también le dará un nuevo nivel de comprensión del mundo de .NET GC.
GEOCHET

Respuestas:

51

Uso MemProfiler de Scitech cuando sospecho 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 lenguaje o plataforma, si escribe un código incorrecto, suceden cosas malas.

GEOCHET
fuente
3
Sí, tuve una oportunidad con este, y me ayudó a llegar al fondo de algunas filtraciones difíciles. Las mayores filtraciones que causé fueron causadas por bibliotecas de terceros en el código no administrado al que accedieron mediante interoperabilidad. Me impresionó que esta herramienta detectara fugas en el código no administrado, así como en el código administrado.
Scott Langham
1
He aceptado esto como la respuesta porque es lo que funcionó para mí al final, pero creo que todas las otras respuestas son muy útiles. Por cierto, esta herramienta se llama más comúnmente Mem Profiler de SciTech.
Scott Langham
41

Solo por el problema de olvidar deshacerse, pruebe la solución descrita 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
Jay Bazuzi
fuente
Preferiría lanzar una excepción en lugar de Debug.Fail
Pedro77
17

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

Descubrimos que .NET Garbage Collector es muy "seguro" en la limpieza de objetos en memoria (como debería ser). Mantendría objetos alrededor solo porque podríamos estar usándolo en algún momento en el futuro. Esto significaba que teníamos que tener más cuidado con la cantidad de objetos que inflamos en la memoria. Al final, convertimos todos nuestros objetos de datos en 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 adicional de lo que quiero decir con "inflar bajo demanda". En nuestro modelo de objetos de nuestra base de datos, utilizamos las propiedades de un objeto primario 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" de forma individual, 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 creó algunos problemas reales de memoria y rendimiento cuando había muchos registros en la memoria. Entonces 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 al limitar los accesos a la base de datos y una gran ganancia en espacio de memoria.

marca
fuente
Yo secundo este producto. Fue uno de los mejores perfiladores que he usado.
Gord
Encontré que el generador de perfiles es bastante bueno para mirar los problemas de rendimiento. Sin embargo, las herramientas de análisis de memoria eran bastante pobres. Encontré una fuga con esta herramienta, pero fue una basura ayudarme a identificar la causa de la fuga. Y no le ayuda en absoluto si la fuga está en un código no administrado.
Scott Langham
Ok, la nueva versión 5.1 es muchísimo mejor. Es mejor para ayudarlo a encontrar la causa de la fuga (aunque todavía hay un par de problemas que ANTS me ha dicho que solucionará en la próxima versión). Sin embargo, todavía no hace código no administrado, pero si no le preocupa el código no administrado, esta es una herramienta bastante buena.
Scott Langham
7

Aún debe preocuparse por la memoria cuando escribe 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 administración de memoria en .NET. Segundo, aprenda a usar una herramienta como CLRProfiler (Microsoft). Esto puede darle una idea de lo que está causando la pérdida de memoria (por ejemplo, puede echar un vistazo a la fragmentación del montón de objetos grandes)

Zac Gochenour
fuente
Sí. CLRPRofiler es bastante genial. Puede ser un poco explosivo con la información cuando se intenta explorar la vista que le brinda de los objetos asignados, pero todo está ahí. Definitivamente es un buen punto de partida, especialmente porque es gratis.
Scott Langham
6

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

Sin embargo, es posible que la memoria utilizada por una aplicación no se libere, por lo que la asignación de memoria de una aplicación puede crecer a lo largo de la vida útil de la aplicación.

De Cómo identificar pérdidas de memoria en Common Language Runtime en Microsoft.com

Se puede producir una pérdida de memoria en una aplicación .NET Framework cuando usa 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 solo puede parecer que tiene una pérdida de memoria. Esta condición puede ocurrir si se declaran muchos objetos grandes (como los objetos DataTable) y luego se agregan a una colección (como un DataSet). Es posible que los recursos que poseen estos objetos nunca se liberen, y los recursos se dejan vivos para 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 desea ver algunas de las estrategias para lidiar con la administración de memoria, le sugiero que busque IDisposable, XNA, administración de memoria ya que los desarrolladores de juegos deben tener una recolección de basura más predecible y, por lo tanto, deben obligar al GC a hacer lo suyo.

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

Timothy Lee Russell
fuente
55
Ver blogs.msdn.com/tess/archive/2006/01/23/… . Realmente no importa si la pérdida de memoria es "tradicional" o no, sigue siendo una pérdida.
Constantin
2
Comprendo su punto de vista, pero la asignación y la reutilización ineficaces de la memoria por parte de un programa es diferente a una pérdida de memoria.
Timothy Lee Russell
buena respuesta, gracias por recordarme que los controladores de eventos pueden ser peligrosos.
frameworkninja
3
@Timothy Lee Russel: Si una cantidad ilimitada (1) de memoria puede permanecer asignada (rooteada) simultáneamente después de volverse inútil (2), sin que nada en el sistema tenga la información y el ímpetu necesarios para desrootearla de manera oportuna, es una pérdida de memoria . Incluso si la memoria pudiera liberarse algún día, si se acumularan suficientes cosas inútiles para ahogar el sistema antes de que eso suceda, es una fuga. (1) Mayor que O (N), siendo N la cantidad de asignación útil; (2) Las cosas no sirven de nada si eliminar las referencias no afectaría la funcionalidad del programa.
supercat
2
@Timothy Lee Russel: El patrón normal de "pérdida de memoria" ocurre cuando una entidad retiene la memoria en nombre de otra entidad , esperando que se le diga cuándo ya no es necesaria, pero esta última abandona la entidad sin decirle a la primera. La entidad que tiene la memoria realmente no la necesita, pero no hay forma de determinar eso.
supercat
5

Este blog tiene algunos tutoriales realmente maravillosos que usan windbg y otras herramientas para rastrear fugas de memoria de todo tipo. Excelente lectura para desarrollar tus habilidades.

twk
fuente
5

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

Primero, probé MemProfiler . Lo encontré realmente difícil de usar y nada fácil de usar.

Luego, utilicé JustTrace, que es más fácil de usar y le brinda más detalles sobre los objetos que no están dispuestos correctamente.

Me permitió resolver la pérdida de memoria con mucha facilidad.

billybob
fuente
3

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 se libere memoria 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, por lo que diseñar el código de limpieza adecuado para su situación suele ser el mejor enfoque.

Chris Ballard
fuente
3

Prefiero dotmemory de Jetbrains

josepainumkal
fuente
puedes ser el único :)
HellBaby
Yo también lo intenté. Creo que esta es una buena herramienta. Fácil de usar, informativo. Se integra a Visual Studio
redimido
En nuestro caso, al solucionar problemas de pérdidas de memoria, la herramienta Instantánea de Visual Studio se bloqueó / no se realizó una instantánea. Dotmemory mantuvo la calma y manejó múltiples instantáneas de más de 3 GB con (aparentemente) facilidad.
Michael Kargl
3

Big guns - Herramientas de depuración para Windows

Esta es una increíble colección de herramientas. Puede analizar los 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 la memoria. Solo tuve que crear un volcado de memoria completa del proceso vivo que se ejecuta en el servidor de producción, todo el análisis se realizó sin conexión en WinDbg. (Resultó que algunos desarrolladores estaban usando en exceso el almacenamiento de sesión en memoria).

El blog "Si está roto es ..." tiene artículos muy útiles sobre el tema.

Constantin
fuente
2

Lo mejor a tener en cuenta es realizar un seguimiento de las referencias a sus objetos. Es muy fácil terminar colgando referencias a objetos que ya no te importan. Si ya no va a usar algo, deshágase de él.

Acostúmbrese a usar un proveedor de caché con vencimientos deslizantes, de modo que si algo no se hace referencia durante un período de tiempo deseado, se desreferencia y se limpia. Pero si se accede mucho, dirá en la memoria.

Gord
fuente
2

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 sos complemento para analizar la memoria del proceso, los hilos y las pilas de llamadas.

También puede usar este método para identificar problemas en los servidores, después de instalar las herramientas, comparta el directorio, luego conéctese al recurso compartido desde el servidor utilizando (uso neto) y realice un bloqueo o cuelgue el proceso.

Luego analice sin conexión.

Stuart McConnell
fuente
Sí, esto funciona bien, especialmente para cosas más avanzadas o para diagnosticar problemas en el software lanzado al que no puede adjuntar fácilmente un depurador. Este blog tiene muchos consejos sobre cómo usar bien estas herramientas: blogs.msdn.com/tess
Scott Langham
2

Después de una de mis correcciones para la aplicación administrada tuve lo mismo, como cómo verificar que mi aplicación no tendrá la misma pérdida de memoria después de mi próximo cambio, así que escribí algo como el marco de verificación de liberación de objetos, por favor, eche un vistazo el paquete NuGet ObjectReleaseVerification . Puede encontrar una muestra aquí https://github.com/outcoldman/OutcoldSolutions-ObjectReleaseVerification-Sample , e información sobre esta muestra http://outcoldman.ru/en/blog/show/322

outcoldman
fuente
0

Desde Visual Studio 2015, considere utilizar la herramienta de diagnóstico de uso de memoria lista para usar para recopilar y analizar datos de uso de 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 del uso de memoria de los tipos de objetos.

Michael Freidgeim
fuente
0

Una de las mejores herramientas que utilicé es DotMemory. Puede usar esta herramienta como una extensión en VS.Después de ejecutar su aplicación, puede analizar cada parte de la memoria (por Objeto, NameSpace, etc.) que usa su aplicación y tomar una instantánea de eso , Compárelo con otros SnapShots. DotMemory

Rebwar
fuente