'Système.OutOfMemoryException' a été lancé quand il ya encore beaucoup de mémoire libre

C'est mon code:

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

Exception: Une Exception de type 'System.OutOfMemoryException " a été jeté.

j'ai 4 Go de mémoire sur cette machine 2,5 Go est libre quand je commence cette course, il y a clairement assez d'espace sur le PC pour gérer les 762mo de 100000000 nombres aléatoires. J'ai besoin de stocker autant de nombres aléatoires que possible étant donné la mémoire disponible. Quand je vais à la production il y aura 12 Go sur la boîte et je souhaitez en faire usage.

Ne le CLR me contraindre à un défaut de mémoire max pour commencer? et comment puis-je demander de plus?

mise à Jour

j'ai pensé qu'en divisant ceci en plus petits morceaux et en ajoutant progressivement à mes besoins de mémoire aiderait si le problème est dû à fragmentation de la mémoire , mais il ne fait pas Je ne peux pas passer un ArrayList total taille de 256mb indépendamment de ce que je fais peaufinage 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 ma méthode principale:

int blockSize = 1000000;

while (true) {
  try
  {
    AddNDRandomNumbers(blockSize);                    
  }
  catch (System.OutOfMemoryException ex)
  {
    break;
  }
}            
double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0;
77
demandé sur John Saunders 2009-07-20 17:50:53

13 réponses

vous pouvez vouloir lire ceci: " " Out Of Memory "ne se réfère pas à la mémoire physique " par Eric Lippert.

en bref, et très simplifié," Out of memory " ne signifie pas vraiment que la quantité de mémoire disponible est trop petite. La raison la plus courante est que dans l'espace d'adresse actuel, il n'y a aucune partie contiguë de la mémoire qui est assez grande pour servir l'allocation voulue. Si vous avez 100 blocs, chaque 4 Mo de large, qui ne va pas pour vous aider quand vous avez besoin d'un bloc de 5 Mo.

Points Clés:

  • le stockage de données que nous appelons" mémoire de processus "est à mon avis le mieux visualisé comme un fichier massif sur le disque .
  • RAM peut être considéré comme une simple optimisation des performances
  • la quantité totale de mémoire virtuelle que votre programme consomme n'est vraiment pas très pertinente pour sa performance
  • "manquer de mémoire vive" entraîne rarement une erreur de" mémoire vive". Au lieu d'une erreur, il en résulte de mauvaises performances parce que le coût total du fait que le stockage est effectivement sur le disque devient soudainement pertinent.
116
répondu Fredrik Mörk 2015-01-14 17:38:57

vous n'avez pas de bloc continu de mémoire pour allouer 762MO, votre mémoire est fragmentée et l'allocateur ne peut pas trouver un trou assez grand pour allouer la mémoire nécessaire.

  1. Vous pouvez essayer de travailler avec le commutateur /3GB (comme d'autres l'ont suggéré)
  2. ou passez à OS 64 bits.
  3. ou modifier l'algorithme pour qu'il n'ait pas besoin d'un gros morceau de mémoire. peut-être allouer quelques petits morceaux (relativement) de mémoire.
23
répondu Shay Erlichmen 2009-07-20 13:59:45

vérifiez que vous construisez un processus 64 bits, et non un 32 bits, qui est le mode de compilation par défaut de Visual Studio. Pour ce faire, faites un clic droit sur votre projet, Propriétés -> construire -> plateforme cible : x64. Comme tout processus 32 bits, les applications Visual Studio compilées en 32 bits ont une limite de mémoire virtuelle de 2 Go.

processus 64 bits n'ont pas cette limitation, car ils utilisent des pointeurs 64 bits, de sorte que leur espace d'adresse maximale théorique (la taille de leur mémoire virtuelle) est de 16 exaoctets (2^64). En réalité, Windows x64 limite la mémoire virtuelle des processus à 8To. La solution au problème de la limite de mémoire est alors de compiler en 64 bits.

cependant, la taille de L'objet dans Visual Studio est encore limitée à 2 Go, par défaut. Vous serez en mesure de créer plusieurs tableaux dont la taille combinée sera supérieure à 2 Go, mais vous ne pouvez pas par défaut créer des tableaux plus grands que 2 Go. Avec un peu de chance, si vous voulez encore créer des tableaux plus grands que 2 Go, vous pouvez le faire en ajoutant le code suivant à votre application.fichier de configuration:

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>
18
répondu Shift Technology 2013-06-26 13:56:06

comme vous avez probablement compris, le problème est que vous essayez d'allouer un grand bloc contigu de mémoire, qui ne fonctionne pas en raison de la fragmentation de la mémoire. Si je devais faire ce que vous faites, je ferais ce qui suit:

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

ensuite, pour obtenir un indice particulier, vous utiliserez randomNumbers[i / sizeB][i % sizeB] .

une autre option si vous accédez toujours aux valeurs dans l'ordre pourrait être d'utiliser le constructeur surchargé pour spécifier la valeur de départ. De cette façon, vous obtiendriez un nombre semi aléatoire (comme le DateTime.Now.Ticks ) stockez-le dans une variable, puis quand vous commencez à parcourir la liste, vous créez une nouvelle instance aléatoire en utilisant le seed 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);
}

il est important de noter que bien que le blog lié dans la réponse de Fredrik Mörk indique que le problème est généralement due à un manque de adresse espace il n'énumère pas un certain nombre d'autres questions, comme la limitation de la taille de L'objet CLR de 2 Go (mentionné dans un commentaire de ShuggyCoUk sur le même blog), passe sous silence la fragmentation de la mémoire, et ne mentionne pas l'impact de la taille du fichier page (et comment il peut être abordé avec l'utilisation de la CreateFileMapping fonction ).

la limite de 2GB signifie que randomNumbers doit être inférieur à 2GB. Puisque les tableaux sont des classes et ont certains au-dessus eux - mêmes cela signifie un tableau de double devra être inférieure à 2^31. Je ne suis pas sûr de combien plus petit que 2^31 La longueur devrait être, mais au-dessus d'un tableau. net? indique 12 - 16 octets.

fragmentation de la Mémoire est très similaire à la fragmentation du disque dur. Vous pouvez avoir 2Go d'espace d'adresse, mais comme vous créez et détruisez des objets, il y aura des écarts entre les valeurs. Si ces lacunes sont trop petites pour votre grand objet, et l'espace supplémentaire ne peut pas être demandée, alors vous obtiendrez le System.OutOfMemoryException . Par exemple, si vous créez 2 millions, objets de 1024 octets, alors vous utilisez 1,9 Go. Si vous supprimez chaque objet dont l'adresse n'est pas un multiple de 3, alors vous utiliserez .6 Go de mémoire, mais il sera étalé à travers l'espace d'adresse avec 2024 octets blocs ouverts entre les deux. Si vous avez besoin de créer un objet qui était .2 Go vous ne seriez pas en mesure de le faire parce qu'il n'y a pas un bloc assez grand pour l'adapter et l'espace supplémentaire ne peut pas être obtenu (en supposant un environnement de 32 bits). Les solutions possibles à ce problème sont des choses comme l'utilisation d'objets plus petits, la réduction de la quantité de données que vous stockez en mémoire, ou l'utilisation d'un algorithme de gestion de mémoire pour limiter/prévenir la fragmentation de la mémoire. Il convient de noter que, sauf si vous êtes l'élaboration d'un vaste programme qui utilise une grande quantité de mémoire, ce ne sera pas un problème. En outre, ce problème peut se poser sur les systèmes 64 bits car windows est limité principalement par la taille du fichier page et la quantité de RAM sur le système.

étant donné que la plupart des programmes demandent de la mémoire de travail à L'OS et ne demandent pas de mappage de fichier, ils seront limités par la RAM du système et la taille des fichiers de page. Comme indiqué dans le commentaire de Néstor Sánchez (Néstor Sánchez) sur le blog, avec le code géré comme C# vous êtes collé à la limitation de fichier RAM/page et l'espace d'adresse du système d'exploitation.


C'était bien plus long que prévu. Espérons que cela aide quelqu'un. Je l'ai posté parce que j'ai couru dans le System.OutOfMemoryException en lançant un programme x64 sur un système avec 24 Go de RAM même si mon tableau ne tenait que 2 Go de trucs.

7
répondu Trisped 2017-05-23 10:31:11

je déconseille l'option de démarrage windows de /3 Go. En dehors de tout le reste (il est exagéré de faire cela pour un application mal comportée, et il ne sera probablement pas résoudre votre problème de toute façon), il peut causer beaucoup d'instabilité.

de nombreux pilotes Windows ne sont pas testés avec cette option, donc un bon nombre d'entre eux supposent que les pointeurs mode utilisateur pointent toujours vers les 2 Go inférieurs de l'espace d'adresse. Ce qui veut dire qu'ils peuvent se casser horriblement avec /3GB.

cependant, Windows limite normalement un processus de 32 bits à un espace d'adresse de 2 Go. Mais cela ne signifie pas que vous devez vous attendre à être en mesure d'allouer 2 Go!

L'espace d'adressage est déjà jonché de toutes sortes de données allouées. Il y a la pile, et tous les assemblages qui sont chargés, les variables statiques et ainsi de suite. Il n'y a aucune garantie qu'il y aura 800 Mo de mémoire contiguë non affectée nulle part.

allouant 2 400MO des morceaux serait probablement mieux. Ou 4 morceaux de 200 Mo. Il est beaucoup plus facile de trouver de la place dans un espace de mémoire fragmenté.

de toute façon, si vous allez déployer ceci sur une machine de 12 Go de toute façon, vous voudrez exécuter ceci comme une application 64 bits, ce qui devrait résoudre tous les problèmes.

5
répondu jalf 2009-07-20 14:05:46

passer de 32 à 64 bits a fonctionné pour moi - mérite un essai si vous êtes sur un PC 64 bits et il n'a pas besoin de port.

3
répondu chris 2012-08-31 21:04:27

si vous avez besoin de telles grandes structures, peut-être pourriez-vous utiliser des fichiers mappés en mémoire. Cet article pourrait s'avérer utile: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

LP, Dejan

2
répondu Dejan Stanič 2009-07-20 14:09:09

32bit windows a une limite de mémoire de processus de 2 Go. L'option de boot /3GB que d'autres ont mentionnée fera ce 3GB avec seulement 1GB restant pour l'utilisation du noyau du système D'exploitation. De façon réaliste, si vous voulez utiliser plus de 2 Go sans tracas, alors un OS 64bit est nécessaire. Cela résout également le problème selon lequel bien que vous puissiez avoir 4 Go de mémoire vive physique, l'espace d'adresse réquisitionné pour la carte vidéo peut rendre un chuck assez important de cette mémoire inutilisable - généralement autour de 500 Mo.

1
répondu redcalx 2009-07-20 14:03:07

plutôt que d'allouer un réseau massif, pourriez-vous essayer d'utiliser un itérateur? Ces valeurs sont exécutées en retard, ce qui signifie que les valeurs ne sont générées que lorsqu'elles sont demandées dans une instruction foreach; vous ne devriez pas manquer de mémoire de cette façon:

private static IEnumerable<double> 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);
}

ce qui précède générera autant de nombres aléatoires que vous le souhaitez, mais ne les générera que s'ils sont demandés via une instruction foreach. Vous ne manquerez pas de mémoire de cette façon.

alternativement, si vous doit avoir tous dans un endroit, de les stocker dans un fichier plutôt que dans la mémoire.

1
répondu Judah Himango 2009-07-20 17:05:39

Eh bien, j'ai eu un problème similaire avec le grand jeu de données et essayer de forcer l'application à utiliser autant de données n'est pas vraiment la bonne option. Le meilleur conseil que je puisse vous donner est de traiter vos données en petit morceau si c'est possible. Parce que traiter autant de données, le problème reviendra tôt ou tard. De plus, vous ne pouvez pas connaître la configuration de chaque machine qui exécutera votre application, donc il y a toujours un risque que l'exception se produise sur un autre pc.

0
répondu Francis B. 2009-07-20 14:05:36

j'ai eu un problème similaire, il était dû à un StringBuilder.ToString ();

0
répondu Ricardo Rix 2017-03-30 10:57:24

Convertissez votre solution En x64. Si vous rencontrez toujours un problème, accordez la longueur maximale à tout ce qui jette une exception comme ci-dessous:

 var jsSerializer = new JavaScriptSerializer();
 jsSerializer.MaxJsonLength = Int32.MaxValue;
0
répondu Samidjo 2017-11-13 13:58:06

augmente la limite du processus Windows à 3 Go. (via boot.ini ou Vista boot manager)

-2
répondu leppie 2009-07-20 13:53:33