example - C#: Excepción de falta de memoria
remarks c# (9)
Dos puntos:
- Si está ejecutando Windows de 32 bits, no tendrá acceso a todos los 4 GB, solo 2 GB.
- No olvide que la implementación subyacente de
List
es una matriz. Si su memoria está muy fragmentada, es posible que no haya suficiente espacio contiguo para asignar suList
, aunque en total tenga mucha memoria libre.
Hoy mi aplicación arrojó hoy una OutOfMemoryException
. Para mí esto siempre fue casi imposible ya que tengo 4GB de RAM y mucha memoria virtual también. El error ocurrió cuando intenté agregar una colección existente a una nueva lista.
List<Vehicle> vList = new List<Vehicle>(selectedVehicles);
A mi entender, no hay mucha memoria asignada aquí ya que los vehículos que mi nueva lista debería contener ya existen dentro de la memoria. Debo admitir que Vehicle
es una clase muy compleja y traté de agregar aproximadamente 50,000 artículos a la nueva lista a la vez. Pero dado que todos los Vehicle
de la aplicación provienen de una base de datos de solo 200 MB de tamaño, no tengo idea de qué puede causar una OutOfMemoryException
en este momento.
Los datos almacenados en la base de datos en comparación con la memoria en su aplicación son muy diferentes.
No hay forma de obtener el tamaño exacto de su objeto, pero podría hacer esto:
GC.GetTotalMemory()
Después de que se haya cargado una cierta cantidad de objetos y vea cuánto está cambiando la memoria a medida que carga la lista.
Si es la lista la que está causando el uso excesivo de memoria, entonces podemos ver formas de minimizarla. Por ejemplo, ¿por qué quieres 50,000 objetos cargados en la memoria todos a la vez en primer lugar. ¿No sería mejor llamar al DB como lo requiera?
Si echa un vistazo aquí: http://www.dotnetperls.com/array-memory también verá que los objetos en .NET son mayores que sus datos reales. Una lista genérica es aún más una memoria que una matriz. Si tiene una lista genérica dentro de su objeto, crecerá aún más rápido.
Mi equipo de desarrollo resolvió esta situación:
Agregamos el siguiente script Post-Build en el proyecto .exe y lo compilamos nuevamente, estableciendo el objetivo en x86 y aumentando en 1.5 gb y también x64 Objetivo de plataforma aumentando la memoria usando 3.2 gb. Nuestra aplicación es de 32 bits.
URL relacionadas:
- http://www.guylangston.net/blog/Article/MaxMemory
- Excepción de falta de memoria de .NET: Usó 1.3GB pero tiene 16GB instalados
Guión:
if exist "$(DevEnvDir)../tools/vsvars32.bat" (
call "$(DevEnvDir)../tools/vsvars32.bat"
editbin /largeaddressaware "$(TargetPath)"
)
Mientras el GC compacta el pequeño montón de objetos como parte de una estrategia de optimización para eliminar agujeros de memoria, el GC nunca compacta el gran montón de objetos por razones de rendimiento ** (el costo de compactación es demasiado alto para objetos grandes (más de 85KB de tamaño) ) **. Por lo tanto, si está ejecutando un programa que usa muchos objetos grandes en un sistema x86, puede encontrar excepciones de OutOfMemory. Si está ejecutando ese programa en un sistema x64, es posible que tenga un montón fragmentado.
No intente traer toda la lista al mismo tiempo, el tamaño de los elementos en la base de datos no es el mismo que el que lleva a la memoria. Si desea procesar los elementos, debe usar un para cada ciclo y aprovechar la carga diferida del marco de entidades para que no incluya todos los elementos en la memoria a la vez. En caso de que quiera mostrar la lista use la paginación (.Skip () y .take ())
OutOfMemoryException (en máquinas de 32 bits) es tan a menudo acerca de la Fragmentación como los límites estrictos reales en la memoria. Encontrará muchas cosas al respecto, pero aquí está mi primer hit de Google discutiéndolo brevemente: http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx . (@Anthony Pegram se refiere al mismo problema en su comentario anterior).
Dicho esto, hay otra posibilidad que le viene a la mente para su código anterior: como está usando el constructor "IEnumerable" para la Lista, es posible que no le dé al objeto ninguna pista sobre el tamaño de la colección que está pasando. al constructor List. Si el objeto que está pasando no es una colección (no implementa la interfaz ICollection
), entonces detrás de escena la implementación de la lista va a tener que crecer varias (o muchas) veces, cada vez dejando atrás una muy pequeña matriz que debe ser recogida de basura. Es probable que el recolector de basura no llegue a esos arrays descartados lo suficientemente rápido y obtendrás tu error.
La solución más simple para esto sería usar el constructor List(int capacity)
para decirle al framework qué tamaño de matriz de respaldo asignar (incluso si está estimando y simplemente adivinando "50000" por ejemplo), y luego usar AddRange(IEnumerable collection)
método para completar su lista.
Por lo tanto, la más simple es "Fix" si estoy en lo correcto: replace
List<Vehicle> vList = new List<Vehicle>(selectedVehicles);
con
List<Vehicle> vList = new List<Vehicle>(50000);
vList.AddRange(selectedVehicles);
Todos los demás comentarios y respuestas se siguen aplicando en términos de decisiones generales de diseño, pero esto podría ser una solución rápida.
Nota (como @Alex comentado a continuación), esto es solo un problema si SelectedVehicles no es una ICollection.
Sé que esta es una vieja pregunta, pero como ninguna de las respuestas menciona el gran montón de objetos, esto podría ser útil para otros que encuentren esta pregunta ...
Cualquier asignación de memoria en .NET de más de 85,000 bytes proviene del gran montón de objetos (LOH), no del montón de objetos pequeños normales. ¿Por qué importa esto? Porque el montón de objetos grandes no está compactado. Lo que significa que el gran montón de objetos se fragmenta y, en mi experiencia, esto conduce inevitablemente a errores de falta de memoria.
En la pregunta original, la lista tiene 50,000 artículos. Internamente, una lista usa una matriz y supone 32 bits que requieren 50,000 x 4 bytes = 200,000 bytes (o el doble que si 64 bits). De modo que la asignación de memoria proviene del gran montón de objetos.
Entonces, ¿qué puede hacer usted al respecto?
Si está utilizando una versión de .net anterior a 4.5.1, entonces todo lo que puede hacer al respecto es conocer el problema y tratar de evitarlo. Entonces, en este caso, en lugar de tener una lista de vehículos, podría tener una lista de vehículos, siempre que ninguna lista haya tenido más de 18,000 elementos en ella. Eso puede llevar a un código feo, pero es una solución viable.
Si está utilizando .net 4.5.1 o posterior, el comportamiento del recolector de basura ha cambiado sutilmente. Si agrega la siguiente línea donde va a realizar asignaciones de memoria grandes:
System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
forzará al recolector de basura a compactar el gran montón de objetos, la próxima vez solamente.
Puede que no sea la mejor solución pero lo siguiente me ha funcionado:
int tries = 0;
while (tries++ < 2)
{
try
{
. . some large allocation . .
return;
}
catch (System.OutOfMemoryException)
{
System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
}
}
por supuesto, esto solo ayuda si tiene disponible la memoria física (o virtual).
Tema de 3 años, pero encontré otra solución de trabajo. Si está seguro de que tiene suficiente memoria libre, ejecuta un sistema operativo de 64 bits y aún obtiene excepciones, asegúrese de configurar esta opción en las propiedades de su proyecto.
.Net4.5 ya no tiene una limitación de 2 GB para los objetos. Agregue estas líneas a App.config
<runtime>
<gcAllowVeryLargeObjects enabled="true" />
</runtime>
y será posible crear objetos muy grandes sin obtener OutOfMemoryException
Tenga en cuenta que solo funcionará en sistemas operativos x64.