tag - Uso de memoria del sitio web de ASP.NET bastante alto
tag helpers asp net core 2 (7)
Fugas de memoria en .NET son todavía posibles. Es cierto que, en su mayor parte, no es necesario liberar objetos (hay algunas excepciones, como el objeto de Gráficos), pero si mantiene las referencias a ellos, no se recogerá la basura, ya que todavía están referenciados.
Si el GC ve que se hace referencia a un objeto en algún lugar, en cualquier lugar de su aplicación, no lo destruirá.
Consulte este artículo de Code Project sobre las fugas de memoria de .NET y cómo localizarlas y corregirlas.
Tengo un sitio web ASP.NET que afectará a la memoria física de 2 gb que se usará en unos 3 o 4 días, lo que para mí suena realmente mal. En este momento, he configurado IIS para reiniciar el proceso del grupo de aplicaciones cuando alcanza los 500 mb. Me gustaría tratar de localizar el problema.
Al crear una nueva instancia de un objeto en .NET, tuve la impresión de que no es necesario liberarlo, ya que el recolector de basura .NET lo hará por mí.
¿Es ese el caso o podría ser una de las razones por las que tengo problemas?
Hay algunas cosas que debes mirar:
En primer lugar, ¿estás usando sesiones? ¿Están en sesiones proc o SQL? Si están en proceso, ¿cuál es el tiempo límite establecido? Si tiene un tiempo de espera realmente largo, esto podría explicar por qué está usando tanta memoria (las sesiones de usuario se almacenarán durante mucho tiempo).
Segundo, la disposición de los objetos. El recolector de basura .NET eliminará las referencias para usted, pero cuando esté creando objetos que implementen la interfaz IDisposable
, siempre debe usar la palabra clave using.
using(Foo foo = new Foo())
{
...
}
es el equivalente de hacer:
Foo foo;
try
{
foo = new Foo();
...
}
finally
{
foo.Dispose();
}
Y se asegurará de que usted deseche sus objetos de manera eficiente.
Si aún no puede encontrar algo obvio en su código, puede crear un perfil de él, comenzando con los métodos que más se llaman. Puedes encontrar información sobre buenos perfiladores here . Eso definitivamente te llevará a la fuente de tu problema.
Puede haber muchas razones para su alto uso de memoria, pero la recolección de basura en .NET es una cosa muy precisa. Es decir, hace mucho por ti, pero a veces no tanto como lo esperas.
Específicamente, solo puede limpiar los objetos a los que no hay referencias activas, así que si ha terminado con una clase, pero algo todavía tiene una referencia a ella, querrá eliminar esa referencia para que el GC pueda recuperar esa memoria. para ti. Además, si tiene conexiones abiertas inactivas (por ejemplo, a una base de datos o algo así), no olvide cerrarlas y eliminarlas. Por lo general, envolvemos estos objetos using
bloques como este:
using(SqlConnection conn = new SqlConnection(myConnString))
{ ...rest of code here }
Esto cerrará automáticamente y eliminará la conexión. Esto se implementa como un bloque try ... finalmente, por lo que la conexión se cerrará incluso si se lanza una excepción en el bloque using.
Aparte de eso, la respuesta es "perfil, perfil, perfil" hasta que encuentre su fuga / cuello de botella / lo que sea.
Si bien es cierto que no tiene que liberar explícitamente la memoria y el recolector de basura hará esto por usted, es posible que el código contenga inadvertidamente una referencia a un objeto para evitar que se recopile, esto es esencialmente una pérdida de memoria.
En ASP.NET, los objetos permanecen "vivos" mientras son referenciados por la aplicación y el caché de sesión. Sin saber nada acerca de su aplicación, le sugeriría que adquiera un generador de perfiles de memoria y observe más de cerca lo que hay en la memoria y si la caché de la aplicación o de la sesión guarda algo que no debería.
Si su aplicación ASP.NET usa eventos (y cuál no lo hace), debe asegurarse de cancelar la suscripción al evento .
Regla de oro:
Si usa +=
para suscribirse a un evento, entonces necesita usar -=
en el método Dispose()
para cancelar la suscripción.
Hay lots resources sobre este tema , y he tenido un poco de dolor en las aplicaciones en las que he trabajado porque su programador habitual no se da cuenta de que los eventos pueden causar pérdidas de memoria.
Use un generador de perfiles de memoria (ya sea en un volcado de memoria o en su entorno en vivo) cuando su memoria exceda los 500 MB para obtener una pista de qué objetos ocupan todo ese espacio.
Al crear perfiles en su entorno de desarrollo, es posible que vea que un tipo de objeto en particular solo crece cuando navega por su sitio, mientras que otros permanecen igual a nivel global.
.NET manejará la recolección de basura para usted de manera muy eficiente. Mientras que en los tipos que implementan IDisposable
, es aconsejable llamar al método Dispose
, probablemente este no sea su problema. Las fugas de memoria pueden ocurrir en .NET por muchas razones. Aquí hay algunos:
- Cachea demasiados datos por usuario en la sesión.
- Usted almacena demasiados datos en la caché de la aplicación o en una variable estática como un diccionario.
- Almacena controles web (o controles de usuario) en el nivel de sesión o aplicación.
- Conecta instancias a eventos en tipos estáticos o tipos a los que se hace referencia (porque se almacenan en un caché).
Espero que esto te dé algunas ideas sobre dónde mirar.
ACTUALIZACIÓN: Debería ver este video sobre la depuración de ASP.NET.
ACTUALIZACIÓN 2: Acerca de su comentario en mi respuesta lo siguiente. El CLR recopilará toda la memoria administrada, por lo tanto, todos los objetos que cree utilizando new
se recopilarán. En este sentido, no importa si un objeto implementa IDisposable
o no. Sin embargo, hay muchas veces que necesita usar recursos nativos (como manejadores de archivos, manejadores gráficos, conexiones de base de datos, uso de memoria nativa, no administrada) directa o indirectamente. El CLR no sabe cómo liberar estos recursos. Para esto .NET tiene la noción de finalizadores. Un finalizador es un método virtual que el desarrollador de una clase puede implementar. Cuando haga esto, el CLR llamará a este método después de que una instancia de ese tipo no tenga referencia y antes de que se recopile. Los finalizadores suelen contener lógica que libera estos recursos. En otras palabras, cuando un tipo necesita recursos nativos, generalmente tendrá un método finalizador que le permite al tipo liberar esos recursos.
¿Qué pasa con el CLR, la historia termina aquí. El CLR no tiene un manejo específico de los objetos que implementan la interfaz IDisposable
. El recolector de basura .NET, sin embargo, es de naturaleza indeterminada. Esto significa que no sabes cuándo se ejecuta y si se ejecuta. Esto significa que puede tomar mucho tiempo antes de que se limpien sus recursos nativos (porque solo se llamará a un finalizador después de una recopilación). Para muchos recursos, sin embargo, es necesario liberarlos tan pronto como haya terminado con ellos. Por ejemplo, tiende a quedarse sin conexiones de base de datos rápidamente cuando no las cierra o cuando está trabajando con GDI + en .NET a través del espacio de nombres System.Drawing).
Por esta razón se introdujo la interfaz IDisposable
. Una vez más, el CLR y el recolector de basura no miran esta interfaz. Es un contrato entre el tipo y sus usuarios, lo que permite a sus usuarios liberar directamente los recursos subyacentes de un objeto. En un diseño normal, tanto el finalizador del objeto como el método Dispose
del objeto llamarán al mismo método privado o protegido que liberará esos recursos. Cuando un tipo se implementa como IDisposable
, es aconsejable llamar al método Dispose
cuando haya terminado con él o envolver el objeto en una declaración using
para permitir que la liberación de recursos nativos sea determinista.
Así que volvamos a tu pregunta. Todos los objetos gestionados serán recopilados por el GC, pero los recursos nativos no. Por lo tanto, los tipos podrían implementar un método finalizador y esos objetos también implementarán la interfaz IDisposable
. Al llamar a Dispose
sobre ellos, se liberarán de forma explícita y directa esos recursos nativos.
Espero que esto tenga sentido.