Pregunta '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 del tipo 'System.OutOfMemoryException'.

Tengo 4GB de memoria en esta máquina 2.5GB es gratis cuando empiezo esta ejecución, hay claramente suficiente espacio 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 trozos más pequeños y agregar incrementalmente a mis requisitos de memoria ayudaría si el problema se debe a fragmentación de la memoria, pero no 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<double> ndRandomNumbers = new List<double>();

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;

76
2017-07-20 13:50


origen


Respuestas:


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 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 programa 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.

115
2017-07-20 13:58



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.

23
2017-07-20 13:59



Compruebe que está creando un proceso de 64 bits, y no uno de 32 bits, que es el modo de compilació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 compiladas 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 compilar 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:

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

18
2018-06-26 13:56



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];
}

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

Otra opción si siempre accede a los valores en orden podría ser usar el constructor sobrecargado para especificar la semilla De esta forma obtendría un número semi aleatorio (como el DateTime.Now.Ticks) almacenarlo en una variable, entonces cuando alguna vez comiences 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 señalar que, si bien el blog vinculado en la respuesta de Fredrik Mörk indica que el problema generalmente se debe a la falta de espacio de dirección no enumera una serie de otros problemas, como la limitación de tamaño de objeto CLR de 2 GB (mencionada en un comentario de ShuggyCoUk en el mismo blog), pasa por alto la fragmentación de memoria y no menciona el impacto del tamaño del archivo de página (y cómo puede ser abordado con el uso de la CreateFileMapping función)

La limitación de 2 GB significa que randomNumbers  debe ser menos de 2GB. Como las matrices son clases y tienen algunos sobre sí mismos, esto significa una matriz de double tendrá que ser más pequeño 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, entonces obtendrá el 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 programa 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 programas 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 el System.OutOfMemoryException ejecutando un programa x64 en un sistema con 24 GB de RAM, aunque mi matriz solo contenía 2 GB de material.


7
2017-12-24 23:15



Aconsejaría contra la opción de arranque de windows / 3GB. Aparte de todo lo demás (es excesivo hacer esto por uno aplicación de 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 pila 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.


5
2017-07-20 14:05



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.


3
2017-08-31 21:04



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


2
2017-07-20 14:09