Compartir a través de


Solución de problemas de memoria insuficiente (System.OutOfMemoryException) en ASP.NET

Este artículo le ayuda a solucionar errores de memoria insuficiente en ASP.NET.

Versión original del producto: ASP.NET
Número de KB original: 2020006

Síntomas

Uno de los problemas más comunes que vemos en los servicios de soporte al cliente de Microsoft es OutOfMemoryException escenarios. Por lo tanto, hemos reunido una colección de recursos para ayudar a solucionar e identificar la causa de problemas de memoria.

Antes de tratar los detalles de la solución de problemas de un OutOfMemoryException, es importante comprender qué causa este problema. Al contrario de lo que muchos desarrolladores creen, la cantidad de RAM instalada no afecta a la posibilidad de un OutOfMemoryException. Un sistema operativo de 32 bits puede abordar 4 GB de espacio de direcciones virtual, independientemente de la cantidad de memoria física instalada en el cuadro. De ese modo, se reservan 2 GB para el sistema operativo (memoria en modo kernel) y se asignan 2 GB a procesos en modo de usuario. Los 2 GB asignados para la memoria en modo kernel se comparten entre todos los procesos, pero cada proceso obtiene sus propios 2 GB de espacio de direcciones en modo de usuario. Todo supone que no se está ejecutando con el /3gb modificador habilitado.

Cuando una aplicación necesita usar memoria, reserva un fragmento del espacio de direcciones virtuales y, a continuación, confirma la memoria de ese fragmento. Es exactamente lo que hace el recolector de elementos no utilizados (GC) de .NET Framework cuando necesita memoria para aumentar los montones administrados. Cuando el GC necesita un nuevo segmento para el montón de objetos pequeños (donde residen los objetos menores de 85 KB), realiza una asignación de 64 MB. Cuando necesita un nuevo segmento para el montón de objetos grandes, realiza una asignación de 16 MB. Estas asignaciones grandes deben cumplirse a partir de bloques contiguos de 2 GB de espacio de direcciones con el que el proceso tiene que trabajar. Si el sistema operativo no puede satisfacer la solicitud del GC para un bloque de memoria contiguo, se produce una System.OutOfMemoryException (OOM).

Nota:

Un proceso de 32 bits que se ejecuta en un sistema operativo de 64 bits puede abordar 4 GB de memoria en modo de usuario y un proceso de 64 bits que se ejecuta en un sistema operativo de 64 bits puede abordar 8 TB de memoria en modo de usuario, por lo que un OOM en un sistema de operaciones de 64 bits no es probable. Es posible experimentar un OOM en un proceso de 32 bits que se ejecuta en un sistema operativo de 64 bits, pero normalmente no se produce hasta que el proceso usa cerca de 3 GB de bytes privados.

Hay dos razones por las que es posible que vea una condición de OOM.

  1. El proceso usa mucha memoria (normalmente más de 800 MB en un entorno de 32 bits).
  2. El espacio de direcciones virtuales está fragmentado, lo que reduce la probabilidad de que una asignación grande y contigua se realice correctamente.

También es posible ver una condición de OOM debido a una combinación de 1 y 2. Para obtener más información, consulte Solución de problemas de System.OutOfMemoryExceptions en ASP.NET.

Cuando se produce una OOM, puede observar uno o varios de los síntomas siguientes:

Cuando se trata de determinar la causa de una condición de OOM, realmente está trabajando para determinar la causa de una situación de memoria alta o un espacio de direcciones fragmentado. Aunque no podemos documentar todas las causas posibles de estas situaciones, hay algunas causas comunes que vemos con regularidad.

En la siguiente información se describen las causas comunes de las condiciones de OOM y las resoluciones para resolver cada una de estas causas.

Concatenación de cadenas

Las cadenas de una aplicación administrada (una aplicación escrita mediante .NET Framework) son inmutables. Cuando se asigna un nuevo valor a una cadena, se realiza una copia de la cadena existente. Y el nuevo valor se asigna a la nueva cadena. Normalmente no causa ningún problema. Pero cuando se concatena un gran número de cadenas, termina causando muchas más asignaciones de cadenas que un desarrollador. Y puede provocar el crecimiento de la memoria y las condiciones de OOM.

Para evitar OOM debido a la concatenación de cadenas, asegúrese de que usa la StringBuilder clase . Para obtener más información, vea Cómo mejorar el rendimiento de la concatenación de cadenas en Visual C#.

Fragmentación en el montón administrado

El recolector de elementos no utilizados (GC) de una aplicación administrada compacta los montones para reducir la cantidad de fragmentación. Sin embargo, es posible anclar objetos en una aplicación administrada. Los objetos anclados no se pueden mover durante la compactación del montón. Si lo hace, cambiaría la dirección en la que se encuentra el objeto. Si una aplicación ancla un gran número de objetos o ancla objetos durante mucho tiempo, puede provocar fragmentación en el montón administrado. Puede provocar que el GC crezca el montón administrado con más frecuencia y provocar una condición de OOM.

Hemos trabajado para minimizar las condiciones de OOM debido al anclaje desde .NET Framework 1.0. Se han realizado mejoras incrementales en cada versión. Sin embargo, todavía hay patrones de diseño que puede implementar que serán beneficiosos si tiene una necesidad de anclar objetos.

Fragmentación en el espacio de direcciones virtuales (VA)

Cada proceso tiene una cantidad determinada de memoria asignada y esa memoria representa el espacio de VA para el proceso. Si el espacio va a fragmentarse, aumenta la probabilidad de que el GC no pueda obtener un bloque grande de memoria contigua para aumentar los montones administrados. Y puede conducir a una condición de OOM.

La fragmentación en el espacio de VA suele deberse a uno o varios de los escenarios siguientes:

  • Cargar los mismos ensamblados en varios dominios de aplicación.

    Si necesita usar un ensamblado en más de una aplicación que se ejecuta en el mismo grupo de aplicaciones, asigne un nombre seguro al ensamblado e instálelo en la GAC. Al hacerlo, asegúrese de que el ensamblado solo se carga en el proceso una vez.

  • Ejecutar una aplicación en producción con el atributo de depuración del <compilation> elemento establecido trueen .

    • El atributo de depuración del <compilation> elemento debe ser false cuando está en producción.
    • Puede usar la <deploy retail="true" /> configuración para asegurarse de que la depuración siempre está deshabilitada en el producto. Para obtener más información, vea Deployment Element (ASP.NET Settings Schema).
  • El uso de scripting en eXtensible Style sheet Language (XSL) transforma o crea XmlSerializers.

    En este caso, los ensamblados dinámicos causados por scripts de transformaciones de lenguaje de hoja de estilos extensibles (XSLT) o XmlSerializers.

Devolver grandes conjuntos de datos

Al usar datos de una base de datos u otro origen de datos, es importante limitar la cantidad de datos devueltos. Por ejemplo, el almacenamiento en caché de un resultado de consulta que devuelve una tabla de base de datos completa para evitar el costo de recuperar partes de datos de la base de datos cuando sea necesario no es un buen enfoque. Si lo hace, puede causar memoria elevada y provocar una condición de OOM. Permitir que un usuario inicie una consulta similar es otra manera común de crear una situación de memoria alta. Por ejemplo, devuelva todos los empleados de una empresa o todos los clientes del estado de Texas con un apellido empezando por la letra S.

Limite siempre la cantidad de datos que se pueden devolver desde una base de datos. No permita consultas como SELECT * FROM. . . , por ejemplo, porque no tiene ningún control sobre la cantidad de datos que se muestran en la página.

Es igualmente importante asegurarse de que no se muestra un gran resultado de datos en elementos de la interfaz de usuario, como el control GridView. Además de la memoria necesaria para los datos devueltos, también consumirá grandes cantidades de datos en cadenas y en elementos de la interfaz de usuario necesarios para representar los resultados. Al implementar la paginación y validar la entrada para que no se devuelvan grandes conjuntos de datos, puede evitar este problema.

Ejecución en un entorno de producción con el seguimiento habilitado

ASP.NET seguimiento es una característica eficaz para solucionar problemas de aplicaciones. Pero nunca debería dejarse en un entorno de producción. ASP.NET seguimiento usa estructuras de datos como DataTables para almacenar información de seguimiento y, con el tiempo, pueden causar una condición de memoria alta que puede provocar OOM.

El seguimiento debe deshabilitarse en un entorno de producción. Para ello, establezca el enabled atributo del <trace> elemento en false en el archivo web.config . La habilitación de la implementación comercial mediante <deploy retail="true" /> también deshabilita el seguimiento en las aplicaciones.

Pérdida de recursos nativos

Muchos recursos administrados también usarán recursos nativos. Dado que el GC no limpia los recursos nativos, un desarrollador es responsable de implementar y llamar al método Dispose para limpiar los recursos nativos. Si usa un tipo que implementa la IDisposable interfaz y no llama al Dispose método , corre el riesgo de perder recursos nativos y provocar una condición de OOM.

Estos objetos deben implementar la iDisposable interfaz y debe llamar al Dispose método en estos objetos cuando ya no los necesite.