‘System.OutOfMemoryException’ se lanzó cuando todavía hay mucha memoria libre

Este es mi código:

int size = 100000000; double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb double[] randomNumbers = new double[size]; 

Excepción: se lanzó la excepción de tipo ‘System.OutOfMemoryException’.

Tengo una memoria de 4GB en esta máquina. 2.5 GB es gratis cuando empiezo a correr, hay espacio suficiente en la PC para manejar los 762 mb de 100000000 números aleatorios. Necesito almacenar tantos números aleatorios como sea posible con la memoria disponible. Cuando vaya a la producción, habrá 12GB en la caja y quiero usarla.

¿El CLR me obliga a una memoria máxima predeterminada para comenzar? y ¿cómo solicito más?

Actualizar

Pensé que dividir esto en fragmentos más pequeños y agregar incrementalmente a mis requisitos de memoria ayudaría si el problema se debe a la fragmentación de la memoria , pero no puedo pasar un tamaño total de ArrayList de 256 mb, independientemente de lo que haga tweaking blockSize .

 private static IRandomGenerator rnd = new MersenneTwister(); private static IDistribution dist = new DiscreteNormalDistribution(1048576); private static List ndRandomNumbers = new List(); private static void AddNDRandomNumbers(int numberOfRandomNumbers) { for (int i = 0; i < numberOfRandomNumbers; i++) { ndRandomNumbers.Add(dist.ICDF(rnd.nextUniform())); } } 

De mi método principal:

 int blockSize = 1000000; while (true) { try { AddNDRandomNumbers(blockSize); } catch (System.OutOfMemoryException ex) { break; } } double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0; 

Es posible que desee leer esto: ” ” Sin memoria “no se refiere a la memoria física ” por Eric Lippert.

En resumen, y muy simplificado, “Sin memoria” no significa realmente que la cantidad de memoria disponible sea demasiado pequeña. La razón más común es que dentro del espacio de direcciones actual, no hay una porción contigua de memoria que sea lo suficientemente grande como para servir a la asignación deseada. Si tiene 100 bloques, cada 4 MB de tamaño grande, eso no lo ayudará cuando necesite un bloque de 5 MB.

Puntos clave:

  • el almacenamiento de datos que llamamos “memoria de proceso” en mi opinión se visualiza mejor como un archivo masivo en el disco .
  • La RAM se puede ver simplemente como una optimización del rendimiento
  • La cantidad total de memoria virtual que consume su progtwig realmente no es muy relevante para su rendimiento
  • “quedarse sin RAM” rara vez da como resultado un error de “falta de memoria”. En lugar de un error, da como resultado un mal rendimiento porque de repente se vuelve relevante el costo total del hecho de que el almacenamiento está realmente en el disco.

No tiene un bloque continuo de memoria para asignar 762 MB, su memoria está fragmentada y el asignador no puede encontrar un agujero lo suficientemente grande como para asignar la memoria necesaria.

  1. Puede intentar trabajar con / 3GB (como otros habían sugerido)
  2. O cambie a sistema operativo de 64 bits.
  3. O modifique el algoritmo para que no necesite una gran cantidad de memoria. tal vez asignar algunos trozos más pequeños (relativamente) de memoria.

Compruebe que está creando un proceso de 64 bits, y no uno de 32 bits, que es el modo de comstackción predeterminado de Visual Studio. Para hacer esto, haga clic derecho en su proyecto, Propiedades -> Construir -> destino de la plataforma: x64. Como cualquier proceso de 32 bits, las aplicaciones de Visual Studio comstackdas en 32 bits tienen un límite de memoria virtual de 2 GB.

Los procesos de 64 bits no tienen esta limitación, ya que utilizan punteros de 64 bits, por lo que su espacio de direcciones máximo teórico (el tamaño de su memoria virtual) es de 16 exabytes (2 ^ 64). En realidad, Windows x64 limita la memoria virtual de los procesos a 8TB. La solución al problema del límite de memoria es comstackr en 64 bits.

Sin embargo, el tamaño del objeto en Visual Studio todavía está limitado a 2 GB, de forma predeterminada. Podrá crear varias matrices cuyo tamaño combinado será superior a 2 GB, pero no puede crear matrices de más de 2 GB de forma predeterminada. Con suerte, si aún desea crear matrices de más de 2 GB, puede hacerlo agregando el siguiente código a su archivo app.config:

      

Como probablemente haya averiguado, el problema es que está tratando de asignar un gran bloque contiguo de memoria, que no funciona debido a la fragmentación de la memoria. Si tuviera que hacer lo que está haciendo, haría lo siguiente:

 int sizeA = 10000, sizeB = 10000; double sizeInMegabytes = (sizeA * sizeB * 8.0) / 1024.0 / 1024.0; //762 mb double[][] randomNumbers = new double[sizeA][]; for (int i = 0; i < randomNumbers.Length; i++) { randomNumbers[i] = new double[sizeB]; } 

Luego, para obtener un índice en particular, usará randomNumbers[i / sizeB][i % sizeB] .

Otra opción si siempre accedes a los valores en orden podría ser utilizar el constructor sobrecargado para especificar la semilla. De esta forma, obtendrías un número semi aleatorio (como DateTime.Now.Ticks ), lo almacenarías en una variable y luego, cuando comenzases a revisar la lista, crearías una nueva instancia aleatoria usando la semilla original:

 private static int randSeed = (int)DateTime.Now.Ticks; //Must stay the same unless you want to get different random numbers. private static Random GetNewRandomIterator() { return new Random(randSeed); } 

Es importante tener en cuenta que aunque el blog vinculado en la respuesta de Fredrik Mörk indica que el problema generalmente se debe a la falta de espacio de direcciones , no enumera una serie de otros problemas, como la limitación del tamaño del objeto CLR de 2 GB (mencionado en un comentario de ShuggyCoUk en el mismo blog), pasa por alto la fragmentación de la memoria y no menciona el impacto del tamaño del archivo de página (y cómo se puede abordar con el uso de la función CreateFileMapping ).

La limitación de 2 GB significa que randomNumbers debe ser inferior a 2 GB. Como las matrices son clases y tienen algunos sobrecargas, esto significa que una matriz de double necesitará ser menor que 2 ^ 31. No estoy seguro de cuánto más pequeño que 2 ^ 31 de la longitud tendría que ser, pero por encima de una matriz .NET? indica 12 - 16 bytes.

La fragmentación de la memoria es muy similar a la fragmentación de HDD. Es posible que tenga 2 GB de espacio de direcciones, pero a medida que crea y destruye objetos, habrá lagunas entre los valores. Si estos espacios son demasiado pequeños para su objeto grande, y no se puede solicitar espacio adicional, obtendrá la System.OutOfMemoryException . Por ejemplo, si crea objetos de 2 millones y 1024 bytes, entonces está utilizando 1.9 GB. Si elimina todos los objetos donde la dirección no es un múltiplo de 3, entonces utilizará .6GB de memoria, pero se extenderá en el espacio de direcciones con bloques abiertos de 2024 bytes entre ellos. Si necesita crear un objeto que fuera .2GB, no podría hacerlo porque no hay un bloque lo suficientemente grande como para caber en él y no se puede obtener espacio adicional (suponiendo un entorno de 32 bits). Las posibles soluciones a este problema son cosas como usar objetos más pequeños, reducir la cantidad de datos que almacena en la memoria, o usar un algoritmo de administración de memoria para limitar / prevenir la fragmentación de la memoria. Cabe señalar que a menos que esté desarrollando un progtwig grande que utiliza una gran cantidad de memoria, esto no será un problema. Además, este problema puede surgir en los sistemas de 64 bits, ya que Windows está limitado principalmente por el tamaño del archivo de página y la cantidad de RAM en el sistema.

Como la mayoría de los progtwigs solicitan memoria de trabajo del sistema operativo y no solicitan una asignación de archivos, estarán limitados por la memoria RAM del sistema y el tamaño del archivo de página. Como se señala en el comentario de Néstor Sánchez (Néstor Sánchez) en el blog, con código administrado como C #, está atascado en la limitación del archivo RAM / página y el espacio de direcciones del sistema operativo.


Eso fue mucho más de lo esperado. Espero que ayude a alguien. Lo publiqué porque me encontré con System.OutOfMemoryException ejecutando un progtwig x64 en un sistema con 24 GB de RAM, aunque mi matriz solo contenía 2 GB de material.

Aconsejaría contra la opción de arranque de windows / 3GB. Además de todo lo demás (es excesivo hacer esto por una aplicación con mal comportamiento, y probablemente no resolverá su problema de todos modos), puede causar mucha inestabilidad.

Muchos controladores de Windows no se prueban con esta opción, por lo que muchos de ellos suponen que los indicadores de modo de usuario siempre apuntan a los menores 2 GB del espacio de direcciones. Lo que significa que pueden romper horriblemente con / 3GB.

Sin embargo, Windows normalmente limita un proceso de 32 bits a un espacio de direcciones de 2 GB. ¡Pero eso no significa que deba esperar poder asignar 2GB!

El espacio de direcciones ya está lleno de todo tipo de datos asignados. Está la stack y todos los ensamblajes que están cargados, variables estáticas, etc. No hay garantía de que habrá 800 MB de memoria contigua sin asignar en ningún lado.

La asignación de 2 trozos de 400MB probablemente les iría mejor. O 4 trozos de 200 MB. Las asignaciones más pequeñas son mucho más fáciles de encontrar en un espacio de memoria fragmentado.

De todos modos, si va a implementar esto en una máquina de 12 GB de todos modos, querrá ejecutar esto como una aplicación de 64 bits, que debería resolver todos los problemas.

Cambiar de 32 a 64 bits funcionó para mí, vale la pena probarlo si está en una PC de 64 bits y no necesita conectarse.

Si necesita estructuras tan grandes, tal vez podría utilizar archivos asignados de memoria. Este artículo podría ser útil: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

LP, Dejan

Las ventanas de 32 bits tienen un límite de memoria de proceso de 2 GB. La opción de arranque / 3GB que otros mencionaron hará que este 3GB con solo 1 gb restante para el uso del núcleo del sistema operativo. De forma realista, si desea utilizar más de 2 GB sin problemas, se requiere un sistema operativo de 64 bits. Esto también supera el problema por el cual, aunque puede tener 4 GB de RAM física, el espacio de direcciones requerido para la tarjeta de video puede hacer que una gran cantidad de esa memoria no se pueda usar, generalmente alrededor de 500 MB.

En lugar de asignar una matriz masiva, ¿podrías intentar utilizar un iterador? Se ejecutan con retraso, es decir, los valores se generan solo cuando se solicitan en una statement foreach; no debería quedarse sin memoria de esta manera:

 private static IEnumerable MakeRandomNumbers(int numberOfRandomNumbers) { for (int i = 0; i < numberOfRandomNumbers; i++) { yield return randomGenerator.GetAnotherRandomNumber(); } } ... // Hooray, we won't run out of memory! foreach(var number in MakeRandomNumbers(int.MaxValue)) { Console.WriteLine(number); } 

Lo anterior generará tantos números aleatorios como desee, pero solo los generará como se soliciten a través de una statement foreach. No se quedará sin memoria de esa manera.

Alternativamente, si debe tenerlos todos en un solo lugar, guárdelos en un archivo en lugar de en la memoria.

Bueno, tengo un problema similar con un gran conjunto de datos y tratar de forzar a la aplicación a utilizar tantos datos no es realmente la opción correcta. El mejor consejo que te puedo dar es que proceses tus datos en pequeños fragmentos si es posible. Debido a que se manejan demasiados datos, el problema volverá tarde o temprano. Además, no puede conocer la configuración de cada máquina que ejecutará su aplicación por lo que siempre existe el riesgo de que la excepción ocurra en otra PC.

Tuve un problema similar, fue debido a un StringBuilder.ToString ();

Convierte tu solución a x64. Si aún tiene un problema, otorgue la longitud máxima a todo lo que arroje una excepción como la siguiente:

  var jsSerializer = new JavaScriptSerializer(); jsSerializer.MaxJsonLength = Int32.MaxValue; 

Aumente el límite del proceso de Windows a 3 gb. (a través de boot.ini o el administrador de arranque de Vista)