2011-12-19 16 views
51

Hoy mi aplicación arrojó un 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.C#: Excepción de memoria agotada

List<Vehicle> vList = new List<Vehicle>(selectedVehicles); 

A mi entender no hay mucha memoria asignada aquí desde los vehículos de 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 unos 50,000 artículos a la nueva lista a la vez. Pero dado que todos los Vehicle s en la aplicación provienen de una base de datos que tiene solo 200 MB de tamaño: no tengo idea de qué puede causar un OutOfMemoryException en este punto.

+7

¿Cuál es el valor (y tipo) de 'selectedVehicles'? – harold

+1

Cuando se lanzó 'OutOfMemoryException', ¿se conectó al proceso con un depurador y ve cuál podría ser el problema? ¿Qué tan grandes * fueron * los objetos? .NET Framework tiene un límite estricto de 2 GB para el tamaño del objeto, menos la sobrecarga consumida por el propio marco. –

+1

¿Es posible que Vehicle sea una estructura en lugar de una clase? –

Respuesta

57

dos puntos:

  1. Si está ejecutando un Windows de 32 bits, no tendrán toda la 4 GB accesible, solamente 2 GB.
  2. 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 su List, aunque en total tenga mucha memoria libre.
+21

Usted solo tiene 2 GB disponibles en Windows de 64 bits también. Esta es una limitación de .NET Framework, no solo del espacio de direcciones de 32 bits. –

+0

+1 para el punto 2. Podría tratar de escribir una lista fragmentada. –

+22

@CodyGray Eso sería 2GB por objeto (matriz), no 2 GB en total. –

10

Los datos almacenados en la base de datos en comparación con la memoria en su aplicación son muy diferentes.

No hay una manera de obtener el tamaño exacto de su objeto, pero se podía hacer esto:

GC.GetTotalMemory() 

Después de que se haya cargado una cierta cantidad de objetos y ver lo mucho que su memoria está cambiando a medida que carga la lista.

Si es la lista que está causando el uso excesivo de memoria, entonces podemos buscar 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.

7

OutOfMemoryException (en máquinas de 32 bits) es tan a menudo sobre Fragmentación como límites duros reales en la memoria - encontrará mucho sobre esto, pero aquí está mi primer Google golpear a discutir brevemente que: 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, no hay otra posibilidad que viene a la mente de su código de arriba: Como usted está utilizando el constructor "IEnumerable" a la lista, puede no dar el objeto ninguna pista en cuanto al tamaño de la colección que está pasando al constructor Lista. Si el objeto que está transfiriendo no es una colección (no implementa la interfaz ICollection), detrás de las escenas, la implementación de la lista necesitará crecer varias (o muchas) veces, dejando cada vez un tamaño demasiado pequeño. 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á calculando y simplemente adivinando "50000" por ejemplo), y luego usar el método AddRange(IEnumerable collection) para en realidad puebla tu lista.

Por lo tanto, más simple "arreglo" si estoy en lo cierto: sustituir

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 siguen siendo válidas 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 un ICollection.

+3

Si selectedVehicles es una colección, el constructor asignará el corregir el tamaño de la matriz. No es necesario pasar por AddRange. – Alex

3

No intente traer toda la lista de una vez, 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 a para cada ciclo y aprovechar la carga diferida del marco de entidades para no incluir todos los elementos en la memoria a la vez. En caso de que quiera para mostrar la lista de utilización de paginación (Saltee() y .take())

58

.Net4.5 no tiene una limitación de 2 GB para los objetos más. Añadir estas líneas a App.config

<runtime> 
    <gcAllowVeryLargeObjects enabled="true" />  
</runtime> 

y será posible crear objetos muy grandes sin conseguir OutOfMemoryException

Tenga en cuenta que sólo funcionará en x64 de OS!

+0

Bien hecho. Esto funcionó para mí, notando que tenía que cambiar el objetivo de compilación a x64. –

+0

se aplica también a ASP.NET 4.5? Usando el informe local, * Mi aplicación v1 (asp.net 3.5 - clr 2.0 - clásica) * funciona *** OK ***, pero mi aplicación v2 (asp.net 4.5, clr 4.0, clásica) genera *** OutOfMemoryException error ***, en _el mismo servidor IIS_ – Kiquenet

67

Tema de 3 años, pero encontré otra solución de trabajo. Si está seguro de que tiene suficiente memoria libre, con el SO de 64 bits y todavía conseguir excepciones, asegúrese de establecer esta opción en las propiedades del proyecto Equipo enter image description here

+1

sin imagen presente – TheGameiswar

4

Mi Desarrollo resuelve esta situación:

Añadimos la siguiente secuencia de comandos Post-Build en el proyecto .exe y compilada de nuevo, estableciendo el objetivo en x86 y aumentando en 1.5 gb y también en la plataforma x64 incrementando la memoria usando 3.2 gb. Nuestra aplicación es de 32 bits.

URL relacionados:

Guión:

if exist "$(DevEnvDir)..\tools\vsvars32.bat" (
    call "$(DevEnvDir)..\tools\vsvars32.bat" 
    editbin /largeaddressaware "$(TargetPath)" 
) 
Cuestiones relacionadas