.net - plazo - qué controla el hipocampo
Anatomía de una "fuga de memoria" (15)
Supongo que en un entorno administrado, una fuga sería mantener una referencia innecesaria a una gran parte de la memoria.
Absolutamente. Además, no usar el método .Dispose () en objetos desechables cuando sea apropiado puede causar fugas de memoria. La forma más fácil de hacerlo es con un bloque de uso porque se ejecuta automáticamente .Dispose () al final:
StreamReader sr;
using(sr = new StreamReader("somefile.txt"))
{
//do some stuff
}
Y si crea una clase que usa objetos no administrados, si no está implementando IDisposable correctamente, podría estar causando pérdidas de memoria para los usuarios de su clase.
En perspectiva .NET:
- ¿Qué es una pérdida de memoria ?
- ¿Cómo puede determinar si su aplicación tiene fugas? Cuales son los efectos?
- ¿Cómo se puede prevenir una pérdida de memoria?
- Si su aplicación tiene pérdida de memoria, ¿desaparece cuando el proceso se cierra o se cierra? ¿O las pérdidas de memoria en su aplicación afectan otros procesos en el sistema incluso después de la finalización del proceso?
- ¿Y qué hay del código no administrado al que se accede a través de COM Interop y / o P / Invoke?
¿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 es cuando se adjunta a un recurso y no lo deja ir. Puede hacer esto tanto en codificación administrada como no codificada.
Con respecto a .NET y otras herramientas de programación, ha habido ideas sobre la recolección de basura y otras formas de minimizar las situaciones que harán que su aplicación se filtre. Pero el mejor método para prevenir pérdidas 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 GC y otras magias limpiarán tu desorden es el camino corto hacia las pérdidas de memoria, y será difícil de encontrar más adelante.
Al codificar sin administrar, normalmente se asegura de limpiar, sabe que los recursos que toma, serán su responsabilidad de limpiar, no el conserje.
En .NET, por otro lado, mucha gente piensa que el GC limpiará todo. Bueno, hace algo por ti, pero debes asegurarte de que sea así. .NET envuelve muchas cosas, por lo que no siempre sabe si está tratando con un recurso administrado o no administrado, y necesita asegurarse de con qué está tratando. El manejo de las fuentes, los recursos GDI, el directorio activo, las bases de datos, etc., generalmente es algo que debe tener en cuenta.
En términos manejados, pondré el cuello en la línea para decir que desaparece una vez que se elimina / elimina el proceso.
Sin embargo, veo que mucha gente tiene esto, y realmente espero que esto termine. ¡No puede pedirle al usuario que finalice su aplicación para limpiar su desorden! Eche un vistazo a un navegador, que puede ser IE, FF, etc., luego abra, por ejemplo, Google Reader, deje que permanezca durante algunos días y vea lo que sucede.
Si luego abre otra pestaña en el navegador, navega a algún sitio, luego cierra la pestaña que albergaba la otra página que hizo que el navegador se filtrara, ¿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 (aproximadamente 3-4 días) si uso Google Reader. Algunas páginas de noticias son aún peores.
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 cosas más en las otras preguntas ...
Cómo entender si su aplicación tiene fugas
Una forma interesante es abrir perfmon y agregar trazas para # bytes en todos los montones y colecciones de # Gen 2 , en cada caso mirando solo su proceso. Si el ejercicio de una función en particular aumenta el total de bytes, y esa memoria permanece asignada después de la próxima colección Gen 2, se podría decir que la función pierde memoria.
Como prevenir
Se han dado otras buenas opiniones.
Solo agregaría que quizás la causa
más comúnmente ignorada
de las pérdidas 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 a 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 controladores de eventos (utilizando la sintaxis
-=
en C #).
¿La fuga desaparece cuando finaliza el proceso, y qué pasa con la interoperabilidad COM?
Cuando finaliza el proceso, el sistema operativo recupera toda la memoria asignada a su espacio de direcciones, incluidos los objetos COM que se envían desde las DLL. Comparativamente raramente, los objetos COM pueden ser servidos desde procesos separados. En este caso, cuando finaliza su proceso, aún puede ser 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 después de que se haya completado. He descubierto que esto puede suceder en su aplicación si está utilizando Windows API y COM (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 he descubierto que no remarcar después de usar ciertos objetos como bolígrafos puede causar el problema.
Personalmente, he sufrido excepciones de falta de memoria que pueden ser causadas pero que no son exclusivas de pérdidas de memoria en aplicaciones de red de puntos. (OOM también puede provenir de pinning ver Pinning Artical ). Si no recibe errores de OOM o necesita confirmar si es una pérdida de memoria que lo está causando, entonces la única forma es perfilar su aplicación.
También trataría de asegurar lo siguiente:
a) Todo lo que implemente Idisposable se desecha usando un bloque final o la declaración de uso, que incluyen pinceles, bolígrafos, etc. (algunas personas argumentan que no se debe agregar nada)
b) Cualquier cosa que tenga un método de cierre se cierra nuevamente usando finalmente o la declaración de uso (aunque he encontrado 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 que se tratan correctamente después. (algunos tienen métodos de limpieza para liberar recursos)
Espero que esto ayude.
Encontré memprofiler una muy buena ayuda al encontrar pérdidas de memoria en .Net. No es gratis como el Microsoft CLR Profiler, pero en mi opinión es más rápido y más preciso. UNA
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 el uso de memoria y determinar que si está administrando mucha memoria cuando no debería, podría decir que tiene una fuga.
En términos manejados, pondré el cuello en la línea para decir que desaparece una vez que se elimina / 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.
Estrictamente hablando, una pérdida de memoria está consumiendo memoria que "ya no se usa" por el programa.
"Ya no se usa" tiene más de un significado, podría significar "no más referencias a él", es decir, totalmente irrecuperable, o podría significar, referenciado, recuperable, no utilizado, pero el programa mantiene 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 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 pasar de 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 el monitoreo muestra que cada vez se asigna más memoria a su proceso después de cada ciclo de recolección de basura . En tal caso, está guardando demasiado en la memoria o se pierde alguna implementación subyacente no administrada.
Para la mayoría de las filtraciones, los recursos se recuperan cuando se termina el proceso, sin embargo, algunos recursos no siempre se recuperan en algunos casos precisos, los identificadores de 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 lo libere o finalice.
La mejor explicación de cómo funciona el recolector de basura está en Jeff Richters CLR a través del libro C # , (Cap. 20). Leer esto proporciona una gran base para comprender cómo persisten los objetos.
Una de las causas más comunes de enraizamiento accidental de objetos es conectar eventos fuera de una clase. Si conecta un evento externo
p.ej
SomeExternalClass.Changed += new EventHandler(HandleIt);
y se olvide de desengancharlo cuando disponga, entonces SomeExternalClass tiene una referencia a su clase.
Como se mencionó anteriormente, el memprofiler es excelente para mostrarle las raíces de los 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 haga algo que cree que eliminará los objetos de ese tipo (por ejemplo, cerrar una ventana).
Es útil aquí tener un botón de depuración en algún lugar que ejecute
System.GC.Collect()
un par de veces.
Luego ejecute
!dumpheap -stat -type <TypeName>
nuevamente.
Si el número no bajó, o no bajó tanto como esperaba, entonces tiene una base para una mayor investigación.
(Recibí este consejo de un seminario impartido por
Ingo Rammer
).
La mejor explicación que he visto está en el Capítulo 7 del libro electrónico gratuito Fundamentos de la programación .
Básicamente, en .NET se produce una pérdida de memoria cuando los objetos referenciados están enraizados y, por lo tanto, no se pueden recolectar basura. Esto ocurre accidentalmente cuando mantiene referencias más allá del alcance previsto.
Sabrás que tienes fugas cuando comiences a recibir OutOfMemoryExceptions o el uso de tu memoria va más allá de lo que esperas ( PerfMon tiene buenos contadores de memoria).
Comprender el modelo de memoria de .NET es su mejor manera de evitarlo. Específicamente, entendiendo 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 las dificultades comunes, probablemente los eventos más comunes. Si el objeto A está registrado en un evento en el objeto B , el objeto A se quedará hasta que el objeto B desaparezca porque B tiene una referencia a A. 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 el anidamiento / referencia de sus objetos para ver de dónde provienen las referencias y qué objeto raíz es responsable ( perfil de hormigas de puerta roja , JetBrains dotMemory, memprofiler son realmente buenos opciones, o puede usar WinDbg y SOS de solo texto , pero recomiendo encarecidamente un producto comercial / visual a menos que sea un verdadero gurú).
Creo que el código no administrado está sujeto a sus pérdidas de memoria típicas, excepto que las referencias compartidas son administradas por el recolector de basura. Podría estar equivocado sobre este último punto.
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 se administra o administra, 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 memoria, para reemplazar ADPlus, llamado DebugDiag.
Supongo que en un entorno administrado, una fuga sería mantener una referencia innecesaria a una gran parte de la memoria.
También tenga en cuenta que .NET tiene dos montones, uno es el montón de objetos grandes. Creo que se colocan objetos de aproximadamente 85k o más en este montón. Este montón tiene reglas de vida diferentes que el montón normal.
Si está creando grandes estructuras de memoria (Diccionario o Lista), 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 aún tiene el recurso abierto.
Los objetos COM pueden ser complicados.
Si siempre usa el patrón
IDispose
, estará seguro.
Pero me he encontrado con algunos ensamblados de interoperabilidad que implementan
IDispose
.
La clave aquí es llamar a
Marshal.ReleaseCOMObject
cuando haya terminado con él.
Los objetos COM aún utilizan el recuento de referencias COM estándar.
Todas las pérdidas de memoria se resuelven mediante la terminación del programa.
Pérdida de memoria suficiente y el sistema operativo puede decidir resolver el problema en su nombre.
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. Se puede curar principalmente utilizando 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 .
Usar 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 lleva para la creación de estos objetos, y también para monitorear qué objetos viven en qué parte del montón (fragmentación, LOH, etc.).