Pregunta

En la perspectiva .NET:

  • ¿Qué es un pérdida de memoria?
  • ¿Cómo puede determinar si su aplicación tiene fugas?¿Cuáles son los efectos?
  • ¿Cómo se puede prevenir una pérdida de memoria?
  • Si su aplicación tiene una pérdida de memoria, ¿desaparece cuando el proceso sale o finaliza?¿O las pérdidas de memoria en su aplicación afectan otros procesos en el sistema incluso después de completar el proceso?
  • ¿Y qué pasa con el código no administrado al que se accede a través de COM Interop y/o P/Invoke?
¿Fue útil?

Solución

La mejor explicación que he visto está en el Capítulo 7 del libro gratuito. Libro electrónico Fundamentos de programación..

Básicamente, en .NETO Se produce una pérdida de memoria cuando los objetos a los que se hace referencia están rooteados y, por lo tanto, no se pueden recolectar basura.Esto ocurre accidentalmente cuando se aferra a referencias más allá del alcance previsto.

Sabrá que tiene fugas cuando comience a recibir OutOfMemoryExceptions o cuando su uso de memoria aumente más allá de lo esperado (Monitor de rendimiento tiene bonitos contadores de memoria).

Comprensión .NETOEl modelo de memoria es la mejor manera de evitarlo.Específicamente, comprender cómo funciona el recolector de basura y cómo funcionan las referencias; nuevamente, lo remito al capítulo 7 del libro electrónico.Además, tenga en cuenta los errores comunes, probablemente los más comunes sean los eventos.si objeto A está registrado en un evento en el objeto B, luego objetar A se quedará hasta que el objeto B desaparece porque B tiene una referencia a A.La solución es cancelar el registro de tus eventos cuando hayas terminado.

Por supuesto, un buen perfil de memoria le permitirá ver los gráficos de sus objetos y explorar el anidamiento/referenciación de sus objetos para ver de dónde provienen las referencias y qué objeto raíz es responsable (perfil de hormigas puerta roja, memoria de puntos de JetBrains, perfilador de memoria son muy buenas opciones, o puedes usar la opción de solo texto WinDbg y LLAMADA DE SOCORRO, pero recomiendo encarecidamente un producto comercial/visual a menos que seas un verdadero gurú).

Creo que el código no administrado está sujeto a las típicas pérdidas de memoria, excepto que el recolector de basura administra las referencias compartidas.Podría estar equivocado en este último punto.

Otros consejos

Estrictamente hablando, una pérdida de memoria consume memoria que el programa "ya no utiliza".

"Ya no se usa" tiene más de un significado, podría significar "no más referencia a él", es decir, totalmente irrecuperable, o podría significar, referenciado, recuperable, no utilizado pero el programa mantiene las referencias de todos modos.Sólo lo último se aplica a .Net para objetos perfectamente gestionados.Sin embargo, no todas las clases son perfectas y, en algún momento, una implementación subyacente no administrada podría perder recursos de forma permanente para ese proceso.

En todos los casos, la aplicación consume más memoria de la estrictamente necesaria.Los efectos secundarios, dependiendo de la cantidad filtrada, podrían ir desde ninguno, hasta una desaceleración causada por una recopilación excesiva, una serie de excepciones de memoria y, finalmente, un error fatal seguido de la finalización forzada del proceso.

Sabes que una aplicación tiene un problema de memoria cuando el monitoreo muestra que cada vez se asigna más memoria a tu proceso. después de cada ciclo de recolección de basura.En tal caso, o está manteniendo demasiada memoria o se está filtrando alguna implementación subyacente no administrada.

Para la mayoría de las filtraciones, los recursos se recuperan cuando finaliza el proceso; sin embargo, algunos recursos no siempre se recuperan en algunos casos precisos, los identificadores del cursor GDI son conocidos por eso.Por supuesto, si tiene un mecanismo de comunicación entre procesos, la memoria asignada en el otro proceso no se liberará hasta que ese proceso la libere o finalice.

Creo que las preguntas "qué es una pérdida de memoria" y "cuáles son los efectos" ya han sido respondidas bien, pero quería agregar algunas cosas más sobre las otras preguntas...

Cómo saber si su aplicación tiene fugas

Una forma interesante es abrir rendimiento y agregar rastros para # bytes en todos los montones y # Colecciones Gen 2 , en cada caso mirando solo su proceso.Si el ejercicio de una característica particular hace que el total de bytes aumente y esa memoria permanece asignada después de la siguiente recopilación Gen 2, se podría decir que la característica pierde memoria.

Como prevenir

Se han dado otras buenas opiniones.Sólo añadiría que tal vez el más comúnmente pasado por alto La causa de las pérdidas de memoria de .NET es agregar controladores de eventos a los objetos sin eliminarlos.Un controlador de eventos adjunto a un objeto es una forma de referencia a ese objeto, por lo que evitará la recopilación incluso después de que todas las demás referencias hayan desaparecido.Recuerde siempre desconectar los controladores de eventos (usando el -= sintaxis en C#).

¿La fuga desaparece cuando finaliza el proceso y qué pasa con la interoperabilidad COM?

Cuando el proceso finaliza, el sistema operativo recupera toda la memoria asignada a su espacio de direcciones, incluido cualquier objeto COM servido desde archivos DLL.Es relativamente raro que los objetos COM se puedan servir desde procesos separados.En este caso, cuando su proceso finalice, es posible que aún sea responsable de la memoria asignada en cualquier proceso del servidor COM que haya utilizado.

Definiría las pérdidas de memoria como un objeto que no libera toda la memoria asignada una vez completado.Descubrí que esto puede suceder en su aplicación si está utilizando la API y COM de Windows (es decir,código no administrado que tiene un error o no se administra correctamente), en el marco y en componentes de terceros.También descubrí que no ordenar después de usar ciertos objetos como bolígrafos puede causar el problema.

Personalmente he sufrido excepciones de falta de memoria que pueden deberse, pero no son exclusivas, a pérdidas de memoria en aplicaciones punto net.(OOM también puede provenir de fijar ver Fijación artística).Si no recibe errores de OOM o necesita confirmar si se debe a una pérdida de memoria, la única forma es crear un perfil de su aplicación.

También intentaría asegurarme de lo siguiente:

a) Todo lo que implementa Idisposable se elimina mediante un bloque finalmente o la declaración de uso, que incluye pinceles, bolígrafos, etc. (algunas personas argumentan que además se debe configurar todo en nada)

b) Cualquier cosa que tenga un método de cierre se cierra nuevamente usando finalmente o la declaración de uso (aunque he descubierto que usar no siempre se cierra dependiendo de si declaraste el objeto fuera de la declaración de uso)

c) Si está utilizando código no administrado/API de Windows, estos se tratarán correctamente después.(algunos tienen métodos de limpieza para liberar recursos)

Espero que esto ayude.

Si necesita diagnosticar una pérdida de memoria en .NET, consulte estos enlaces:

http://msdn.microsoft.com/en-us/magazine/cc163833.aspx

http://msdn.microsoft.com/en-us/magazine/cc164138.aspx

Esos artículos describen cómo crear un volcado de memoria de su proceso y cómo analizarlo para que pueda determinar primero si su fuga está administrada o no administrada y, si está administrada, cómo determinar de dónde proviene.

Microsoft también tiene una herramienta más nueva para ayudar a generar volcados de memoria, para reemplazar a ADPlus, llamada DebugDiag.

http://www.microsoft.com/downloads/details.aspx?FamilyID=28bd5941-c458-46f1-b24d-f60151d875a3&displaylang=en

Usando CLR Profiler de Microsoft http://www.microsoft.com/downloads/details.aspx?familyid=86ce6052-d7f4-4aeb-9b7a-94635beebdda&displaylang=en es una excelente manera de determinar qué objetos contienen memoria, qué flujo de ejecución conduce a la creación de estos objetos y también monitorear qué objetos se encuentran en qué lugar del montón (fragmentación, LOH, etc.).

La mejor explicación de cómo funciona el recolector de basura está en Jeff Richters CLR a través de C# libro, (Cap.20).Leer esto brinda una excelente base para comprender cómo persisten los objetos.

Una de las causas más comunes de rootear objetos accidentalmente es conectar eventos fuera de una clase.Si conectas un evento externo

p.ej.

SomeExternalClass.Changed += new EventHandler(HandleIt);

y olvídate de desengancharlo cuando lo deseches, entonces SomeExternalClass tiene una referencia para tu clase.

Como se mencionó anteriormente, el Perfilador de memoria SciTech es excelente para mostrarle las raíces de objetos que sospecha que tienen fugas.

Pero también hay una forma muy rápida de verificar un tipo en particular: simplemente use WnDBG (incluso puede usar esto en la ventana inmediata de VS.NET mientras está conectado):

.loadby sos mscorwks
!dumpheap -stat -type <TypeName>

Ahora haz algo que creas que eliminará los objetos de ese tipo (p. ej.cerrar una ventana).Es útil aquí tener un botón de depuración en algún lugar que se ejecute System.GC.Collect() un par de veces.

Entonces corre !dumpheap -stat -type <TypeName> de nuevo.Si el número no bajó, o no bajó tanto como esperaba, entonces tiene una base para una mayor investigación.(Obtuve este consejo de un seminario impartido por Ingo Rammer).

Supongo que en un entorno administrado, una fuga sería mantener una referencia innecesaria a una gran cantidad de memoria.

¿Por qué la gente piensa que una pérdida de memoria en .NET no es lo mismo que cualquier otra pérdida?

Una pérdida de memoria ocurre cuando te conectas a un recurso y no lo sueltas.Puede hacer esto tanto en codificación administrada como no administrada.

Con respecto a .NET y otras herramientas de programación, ha habido ideas sobre la recolección de basura y otras formas de minimizar situaciones que harán que su aplicación tenga fugas.Pero el mejor método para prevenir pérdidas de memoria es comprender el modelo de memoria subyacente y cómo funcionan las cosas en la plataforma que está utilizando.

Creer que GC y otras magias limpiarán tu desorden es el camino más corto hacia las pérdidas de memoria, y será difícil de encontrar más adelante.

Cuando codificas de forma no administrada, normalmente te aseguras de limpiar, sabes que los recursos que utilizas serán tu responsabilidad de limpiar, no la del conserje.

En .NET, por otro lado, mucha gente piensa que el GC limpiará todo.Bueno, hace algo por ti, pero debes asegurarte de que así sea..NET envuelve muchas cosas, por lo que no siempre se sabe si se trata de un recurso administrado o no administrado, y es necesario asegurarse de con qué se está tratando.El manejo de fuentes, recursos GDI, directorio activo, bases de datos, etc., suelen ser cosas a las que debe prestar atención.

En términos administrados, pondré mi cuello en la línea para decir que desaparece una vez que el proceso se mate/elimine.

Sin embargo, veo que mucha gente tiene esto y realmente espero que esto termine.¡No puedes pedirle al usuario que finalice tu aplicación para limpiar tu desorden!Eche un vistazo a un navegador, que puede ser IE, FF, etc., luego abra, digamos, Google Reader, déjelo reposar durante algunos días y observe lo que sucede.

Si luego abre otra pestaña en el navegador, navega a algún sitio y luego cierra la pestaña que alojaba la otra página que provocó la fuga del navegador, ¿cree que el navegador liberará la memoria?No es así con IE.En mi computadora, IE consumirá fácilmente 1 GiB de memoria en un corto período de tiempo (alrededor de 3 a 4 días) si uso Google Reader.Algunas páginas de noticias son incluso peores.

Supongo que en un entorno administrado, una fuga sería que mantengas una referencia innecesaria a una gran parte de la memoria.

Absolutamente.Además, no utilizar el método .Dispose() en objetos desechables cuando sea apropiado puede provocar fugas de memoria.La forma más sencilla de hacerlo es con un bloque de uso porque ejecuta automáticamente .Dispose() al final:

StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
    //do some stuff
}

Y si crea una clase que utiliza objetos no administrados, si no implementa IDisposable correctamente, podría estar provocando pérdidas de memoria para los usuarios de su clase.

Todas las pérdidas de memoria se resuelven mediante la finalización del programa.

Si pierde suficiente memoria, el sistema operativo puede decidir resolver el problema por usted.

Estaré de acuerdo con Bernard en cuanto a lo que sería una fuga de memoria en .net.

Puede perfilar su aplicación para ver su uso de memoria y determinar si está administrando mucha memoria cuando no debería, podría decir que tiene una fuga.

En términos administrados, me jugaré el cuello para decir que desaparece una vez que se finaliza o elimina el proceso.

El código no administrado es su propia bestia y si existe una fuga dentro de él, seguirá una memoria estándar.definición de fuga.

También tenga en cuenta que .NET tiene dos montones, uno de los cuales es el montón de objetos grandes.Creo que en este montón se colocan objetos de aproximadamente 85k o más.Este montón tiene reglas de duración diferentes a las del montón normal.

Si está creando estructuras de memoria grandes (Diccionarios o Listas), sería prudente buscar cuáles son las reglas exactas.

En cuanto a recuperar la memoria al finalizar el proceso, a menos que esté ejecutando Win98 o su equivalente, todo se devuelve al sistema operativo al finalizar.Las únicas excepciones son las cosas que se abren entre procesos y otro proceso todavía tiene el recurso abierto.

Aunque los objetos COM pueden ser complicados.Si siempre usas el IDispose patrón, estarás a salvo.Pero me he encontrado con algunos ensamblajes de interoperabilidad que implementan IDispose.La clave aquí es llamar Marshal.ReleaseCOMObject cuando hayas terminado con esto.Los objetos COM todavía utilizan el recuento de referencias COM estándar.

encontré Perfilador de memoria .Net una muy buena ayuda para encontrar pérdidas de memoria en .Net.No es gratuito como Microsoft CLR Profiler, pero en mi opinión es más rápido y más concreto.A

Una definición es: No se puede liberar memoria inalcanzable, que ya no se puede asignar a un nuevo proceso durante la ejecución del proceso de asignación.En su mayoría, puede curarse mediante técnicas de GC o detectarse mediante herramientas automatizadas.

Para mayor información por favor visite http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top