Anatomía de una "pérdida de memoria"

votos
159

En la perspectiva de .NET:

  • ¿Qué es una fuga de memoria ?
  • ¿Cómo puede determinar si su aplicación tiene fugas? ¿Cuáles son los efectos?
  • ¿Cómo se puede prevenir una fuga de memoria?
  • Si su aplicación tiene pérdida de memoria, ¿desaparece cuando el proceso se cierra o se elimina? ¿O las fugas de memoria en su aplicación afectan otros procesos en el sistema incluso después de la finalización del proceso?
  • ¿Y qué ocurre con el código no administrado al que se accede a través de COM Interop y / o P / Invoke?
Publicado el 01/08/2008 a las 16:12
fuente por usuario
En otros idiomas...                            


15 respuestas

votos
14

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

Respondida el 01/08/2008 a las 16:19
fuente por usuario

votos
9

Coincidiré con Bernard en cuanto a lo que sería una fuga de mem.

Podría hacer un perfil de su aplicación para ver su uso de memoria, y determinar si administra una gran cantidad de memoria cuando no debería, podría decirse que tiene una fuga.

En términos manejados, pondré mi cuello en la línea para decir que se va una vez que el proceso sea eliminado / eliminado.

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

Respondida el 01/08/2008 a las 16:23
fuente por usuario

votos
106

La mejor explicación que he visto es en el Capítulo 7 del libro electrónico gratuito Foundations of Programming .

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

Sabrá que tiene filtraciones cuando comience a recibir OutOfMemoryExceptions o que su uso de memoria vaya más allá de lo que cabría esperar ( PerfMon tiene buenos contadores de memoria).

Comprender el modelo de memoria de .NET es la mejor forma de evitarlo. Específicamente, entender cómo funciona el recolector de basura y cómo funcionan las referencias. De nuevo, lo remito al capítulo 7 del libro electrónico. Además, tenga en cuenta las trampas comunes, probablemente los eventos más comunes. Si el objeto A se ha registrado para un evento en el objeto B , entonces el objeto A se quedarán hasta el objeto B desaparece porque B contiene una referencia a una . La solución es anular el registro de sus eventos cuando haya terminado.

Por supuesto, un buen perfil de memoria le permitirá ver sus gráficos de objetos y explorar la anidación / referencia de sus objetos para ver de dónde provienen las referencias y qué objeto raíz es responsable ( perfil de hormigas red-gate , JetBrains dotMemory, memprofiler son realmente buenos opciones, o puede usar WinDbg y SOS solo de texto , pero recomendaría un producto comercial / visual a menos que sea un verdadero gurú).

Creo que el código no administrado está sujeto a sus fugas de memoria típicas, excepto que las referencias compartidas son administradas por el recolector de basura. Podría estar equivocado sobre este último punto.

Respondida el 01/08/2008 a las 16:28
fuente por usuario

votos
32

Estrictamente hablando, una pérdida de memoria es consumir memoria que "ya no se usa" por el programa.

"Ya no se usa" tiene más de un significado, podría significar "no hacer más referencia", es decir, totalmente irrecuperable, o podría significar, referenciado, recuperable, sin usar, pero el programa conserva las referencias de todos modos. Solo lo posterior se aplica a .Net para objetos perfectamente administrados . Sin embargo, no todas las clases son perfectas y en algún punto una implementación subyacente no administrada podría perder recursos permanentemente 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, a la desaceleración causada por la recolección excesiva, a una serie de excepciones de memoria y finalmente a un error fatal seguido de la terminación forzada del proceso.

Usted sabe que una aplicación tiene un problema de memoria cuando la supervisión muestra que se asigna más y más memoria a su proceso después de cada ciclo de recolección de basura . En tal caso, está guardando demasiado en memoria o 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 de 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.

Respondida el 01/08/2008 a las 18:52
fuente por usuario

votos
18

Definiría las pérdidas de memoria como un objeto que no libera toda la memoria asignada después de que se haya completado. He encontrado que esto puede suceder en su aplicación si está utilizando la API de Windows y COM (es decir, código no administrado que tiene un error o no se está administrando correctamente), en el marco y en componentes de terceros. También descubrí que no hacer tic-tac después de usar ciertos objetos, como las plumas, puede causar el problema.

Personalmente he sufrido excepciones de falta de memoria que pueden ser causadas, pero que no son exclusivas de las pérdidas de memoria en las aplicaciones dot net. (OOM también puede provenir de Fijar Pinning Artical ). Si no está recibiendo errores OOM o necesita confirmar si se trata de una pérdida de memoria, entonces la única forma es perfilar su aplicación.

También intentaré y aseguraré lo siguiente:

a) Todo lo que implementa Idisposable está dispuesto ya sea usando un bloque finally o el enunciado de uso; estos incluyen pinceles, bolígrafos, etc. (algunas personas argumentan que todo se reduce a nada, además)

b) Cualquier cosa que tenga un método cercano se cierra nuevamente utilizando finalmente o la instrucción using (aunque he encontrado que el uso no siempre se cierra dependiendo de si has declarado el objeto fuera de la instrucción using)

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

Espero que esto ayude.

Respondida el 01/08/2008 a las 18:57
fuente por usuario

votos
9

Todas las fugas de memoria se resuelven por la terminación del programa.

La fuga de memoria suficiente y el sistema operativo pueden decidir resolver el problema en su nombre.

Respondida el 04/08/2008 a las 15:09
fuente por usuario

votos
10

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

Absolutamente. Además, no usar el método .Dispose () en objetos desechables cuando sea apropiado puede causar pérdidas de mem. La forma más fácil de hacerlo es con un bloque de uso, ya que automáticamente ejecuta .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 causando pérdidas de memoria para los usuarios de su clase.

Respondida el 05/08/2008 a las 23:47
fuente por usuario

votos
7

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

Si está creando estructuras de memoria de gran tamaño (de diccionario o de lista) sería prudente ir a buscar cuáles son las reglas exactas.

En cuanto a la recuperación de la memoria en la terminación del proceso, a menos que ejecute Win98 o sus equivalentes, todo se devuelve al sistema operativo en la terminación. Las únicas excepciones son las cosas que se abren en el proceso cruzado y otro proceso todavía tiene el recurso abierto.

Los objetos COM pueden ser difíciles aunque. Si siempre usas el IDisposepatrón, estarás a salvo. Pero me he topado con algunas asambleas interoperativas que implementan IDispose. La clave aquí es llamar Marshal.ReleaseCOMObjectcuando hayas terminado. Los objetos COM todavía usan el conteo de referencias COM estándar.

Respondida el 07/08/2008 a las 14:17
fuente por usuario

votos
18

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 primero pueda determinar si su fuga no está administrada o administrada, y si se administra, cómo averiguar de dónde proviene.

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

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

Respondida el 15/08/2008 a las 00:16
fuente por usuario

votos
28

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

Cómo entender si su aplicación tiene fugas

Una forma interesante es abrir perfmon y agregar trazas para # bytes en todas las colecciones de montones y # Gen 2 , buscando en cada caso solo su proceso. Si el ejercicio de una característica particular provoca que aumenten los bytes totales, y que la memoria permanezca asignada después de la próxima colección de Gen 2, puede decir que la función pierde memoria.

Como prevenir

Otras buenas opiniones han sido dadas. Solo agregaría que quizás la causa más común de las fugas de memoria .NET es agregar controladores de eventos a los objetos sin eliminarlos. Un controlador de eventos adjunto a un objeto es una forma de referencia para ese objeto, por lo que evitará la recopilación incluso después de que todas las demás referencias hayan desaparecido. Recuerde siempre separar los manejadores de eventos (usando la -=sintaxis en C #).

¿La fuga desaparecerá cuando el proceso finalice, y qué pasa con la interoperabilidad COM?

Cuando finaliza su proceso, el SO recupera toda la memoria mapeada en su espacio de direcciones, incluidos los objetos COM servidos desde DLL. Comparativamente raramente, los objetos COM pueden ser servidos desde procesos separados. En este caso, cuando finaliza su proceso, puede seguir siendo responsable de la memoria asignada en cualquier proceso de servidor COM que haya utilizado.

Respondida el 15/08/2008 a las 17:11
fuente por usuario

votos
15

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

Respondida el 16/08/2008 a las 20:54
fuente por usuario

votos
6

Encontré .Net Memory Profiler una muy buena ayuda al encontrar fugas de memoria en .Net. No es gratis como Microsoft CLR Profiler, pero es más rápido y más directo en mi opinión. UN

Respondida el 20/08/2008 a las 19:39
fuente por usuario

votos
14

La mejor explicación de cómo funciona el recolector de basura es Jeff Richters CLR a través del libro C # , (Capítulo 20). Leer esto proporciona una gran base para comprender cómo persisten los objetos.

Una de las causas más comunes de enraizamiento de objetos accidentalmente es mediante la conexión de eventos a una clase. Si conecta un evento externo

p.ej

SomeExternalClass.Changed += new EventHandler(HandleIt);

y olvídate de desengancharte cuando desees, entonces SomeExternalClass tiene una referencia a tu clase.

Como se mencionó anteriormente, el perfilador de memoria SciTech es excelente para mostrarle las raíces de los objetos que sospecha que se están escapando.

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

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

Ahora haga algo que crea que eliminará los objetos de ese tipo (por ejemplo, cierre una ventana). Aquí es útil tener un botón de depuración en algún lugar que se ejecutará System.GC.Collect()un par de veces.

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

Respondida el 27/08/2008 a las 15:12
fuente por usuario

votos
10

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

Una pérdida de memoria es cuando se conecta a un recurso y no lo suelta. Puede hacerlo 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 pierda. Pero el mejor método para evitar fugas de memoria es que necesita comprender su modelo de memoria subyacente y cómo funcionan las cosas en la plataforma que está utilizando.

Creer que la 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 sin gestionar, normalmente asegúrate de limpiar, sabes que los recursos que tomas son tu responsabilidad de limpiar, no los del conserje.

En .NET por otro lado, mucha gente piensa que el GC lo limpiará todo. Bueno, hace algo por ti, pero debes asegurarte de que sea así. .NET envuelve muchas cosas, por lo que no siempre se sabe si se trata de un recurso gestionado o no gestionado, y debe asegurarse de qué está tratando. El manejo de fuentes, recursos de GDI, directorio activo, bases de datos, etc. suele ser algo que debe tener en cuenta.

En términos manejados, pondré mi cuello en la línea para decir que se va una vez que el proceso sea eliminado / eliminado.

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, por ejemplo, Google Reader, déjelo permanecer unos días y observe lo que sucede.

Si luego abre otra pestaña en el navegador, navega hacia algún sitio, luego cierra la pestaña que albergaba la otra página que hizo que el navegador se filtre, ¿cree que el navegador liberará la memoria? No es así con IE. En mi computadora IE fácilmente comerá 1 GiB de memoria en un corto período de tiempo (alrededor de 3-4 días) si utilizo Google Reader. Algunas páginas de noticias son aún peores.

Respondida el 01/09/2008 a las 13:19
fuente por usuario

votos
1

Una definición es: No es posible liberar la memoria inalcanzable, que ya no puede ser asignado a un nuevo proceso durante la ejecución del proceso de asignación. En su mayoría se puede curar mediante el uso de técnicas de GC o detectado por herramientas automatizadas.

Para obtener más información, visite http://all-about-java-and-weblogic-server.blogspot.in/2014/01/what-is-memory-leak-in-java.html .

Respondida el 27/08/2014 a las 16:22
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more