Vraag 'System.OutOfMemoryException' is gegenereerd toen er nog voldoende geheugen vrij was


Dit is mijn code:

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

Uitzondering: Er is een uitzondering gemaakt voor het type 'System.OutOfMemoryException'.

Ik heb 4 GB geheugen op deze machine van 2,5 GB is gratis wanneer ik dit begin, is er duidelijk voldoende ruimte op de pc om de 762mb van 100000000 willekeurige nummers te verwerken. Ik moet zoveel mogelijk willekeurige getallen opslaan, gegeven het beschikbare geheugen. Als ik naar de productie ga, zit er 12 GB op de doos en ik wil er gebruik van maken.

Helpt de CLR me om te beginnen met een standaard max geheugen? en hoe vraag ik meer?

Bijwerken

Ik dacht dat ik dit in kleinere stukjes zou breken en dat stapsgewijs toevoegen aan mijn geheugenvereisten zou helpen als het probleem te wijten is geheugen fragmentatie, maar dat doet het niet Ik kan niet voorbij een totale ArrayList-grootte van 256mb komen, ongeacht wat ik blockSize aanpas.

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()));                
  }
}

Van mijn belangrijkste methode:

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


oorsprong


antwoorden:


Misschien wilt u dit lezen: ""Geheugen vol" verwijst niet naar fysiek geheugen"door Eric Lippert.

Kortom, en zeer vereenvoudigd, betekent "onvoldoende geheugen" niet echt dat de hoeveelheid beschikbaar geheugen te klein is. De meest voorkomende reden is dat er binnen de huidige adresruimte geen aangrenzend geheugen is dat groot genoeg is om de gewenste toewijzing te bedienen. Als je 100 blokken hebt, elk 4 MB groot, dan zal dat je niet helpen wanneer je een blok van 5 MB nodig hebt.

Sleutelpunten: 

  • de gegevensopslag die we "procesgeheugen" noemen, wordt naar mijn mening het beste gevisualiseerd als een enorm bestand op schijf.
  • RAM kan worden gezien als slechts een prestatie-optimalisatie
  • De totale hoeveelheid virtueel geheugen die uw programma gebruikt, is echt niet enorm relevant voor de prestaties
  • "bijna geen RAM" resulteert zelden in een "onvoldoende geheugen" -fout. In plaats van een fout, resulteert dit in slechte prestaties, omdat de volledige kosten van het feit dat opslag daadwerkelijk op schijf is, opeens relevant worden.

115
2017-07-20 13:58



Je hebt geen doorlopende geheugenblok om 762MB toe te wijzen, je geheugen is gefragmenteerd en de allocator kan geen voldoende groot gat vinden om het benodigde geheugen toe te wijzen.

  1. Je kunt proberen te werken met / 3GB (zoals anderen hadden voorgesteld)
  2. Of schakel over naar 64-bits besturingssysteem.
  3. Of pas het algoritme aan zodat het geen grote hoeveelheid geheugen nodig heeft. misschien een paar kleinere (relatief) brokken geheugen toewijzen.

23
2017-07-20 13:59



Controleer of u een 64-bits proces aan het bouwen bent en geen 32-bit-proces, wat de standaard compilatiemodus van Visual Studio is. Om dit te doen, klik met de rechtermuisknop op uw project, Eigenschappen -> Opbouw -> platformdoel: x64. Zoals elk 32-bits proces hebben Visual Studio-toepassingen die zijn gecompileerd in 32-bits een virtuele geheugenlimiet van 2 GB.

64-bits processen hebben deze beperking niet, omdat ze 64-bits verwijzingen gebruiken, dus hun theoretische maximale adresruimte (de grootte van hun virtuele geheugen) is 16 exabytes (2 ^ 64). In werkelijkheid beperkt Windows x64 het virtuele geheugen van processen tot 8TB. De oplossing voor het probleem met de geheugenlimiet is dan om te compileren in 64-bit.

De grootte van het object in Visual Studio is echter standaard beperkt tot 2 GB. U kunt verschillende matrices maken waarvan de gecombineerde grootte groter is dan 2 GB, maar u kunt standaard geen arrays groter dan 2 GB maken. Hopelijk, als u nog steeds arrays groter dan 2 GB wilt maken, kunt u dit doen door de volgende code toe te voegen aan uw app.config-bestand:

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

18
2018-06-26 13:56



Zoals u waarschijnlijk wel hebt vastgesteld, is het probleem dat u probeert een groot, aaneengesloten blok geheugen toe te wijzen, wat niet werkt vanwege geheugenfragmentatie. Als ik zou moeten doen wat je doet, zou ik het volgende doen:

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

Vervolgens, om een ​​bepaalde index te krijgen die u zou gebruiken randomNumbers[i / sizeB][i % sizeB].

Een andere optie als u altijd de waarden in de juiste volgorde gebruikt, zou kunnen zijn om te gebruiken de overbelaste constructor om het zaad te specificeren. Op deze manier zou je een semi-willekeurig nummer krijgen (zoals de DateTime.Now.Ticks) sla het op in een variabele, en wanneer je ooit door de lijst gaat, zou je een nieuwe willekeurige instantie maken met behulp van de oorspronkelijke seed:

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

Het is belangrijk op te merken dat, hoewel de blog die in het antwoord van Fredrik Mörk is gekoppeld, aangeeft dat het probleem meestal te wijten is aan een gebrek aan adres ruimte het vermeldt niet een aantal andere problemen, zoals de beperking van de grootte van de 2 GB CLR-objecten (vermeld in een commentaar van ShuggyCoUk op dezelfde blog), verbergt fragmentfragmentatie van het geheugen en vermeldt niet de impact van de grootte van het paginabestand (en hoe het kan worden aangesproken met behulp van de CreateFileMapping functie).

De beperking van 2 GB betekent dat randomNumbers  moet minder zijn dan 2 GB. Omdat arrays klassen zijn en sommige over zich heen hebben, betekent dit een reeks van double moet kleiner zijn dan 2 ^ 31. Ik weet niet zeker hoeveel kleiner dan 2 ^ 31 de lengte zou moeten zijn, maar Overhead van een .NET-array? geeft 12 - 16 bytes aan.

Geheugenfragmentatie lijkt erg op de fragmentatie van de harde schijf. U hebt mogelijk 2 GB adresruimte, maar terwijl u objecten maakt en vernietigt, zijn er hiaten tussen de waarden. Als deze openingen te klein zijn voor uw grote object en er kan geen extra ruimte worden aangevraagd, krijgt u de System.OutOfMemoryException. Als u bijvoorbeeld 2 miljoen, 1024 byte-objecten maakt, gebruikt u 1,9 GB. Als u elk object verwijdert waarvan het adres geen veelvoud van 3 is, gebruikt u .6GB geheugen, maar het wordt verspreid over de adresruimte met daartussen 2024 bytes open blokken. Als u een object van .2GB moet maken, kunt u dit niet doen, omdat er geen blok is dat groot genoeg is om het in te passen en er geen extra ruimte kan worden verkregen (uitgaande van een 32-bits omgeving). Mogelijke oplossingen voor dit probleem zijn dingen als het gebruik van kleinere objecten, het verminderen van de hoeveelheid gegevens die u in het geheugen opslaat of het gebruik van een algoritme voor geheugenbeheer om geheugenfragmentatie te beperken / voorkomen. Opgemerkt moet worden dat, tenzij u een groot programma ontwikkelt dat een grote hoeveelheid geheugen gebruikt, dit geen probleem zal zijn. Dit probleem kan zich ook voordoen op 64-bits systemen, omdat vensters meestal worden beperkt door de bestandsgrootte van de pagina en de hoeveelheid RAM op het systeem.

Omdat de meeste programma's werkgeheugen aanvragen bij het besturingssysteem en geen bestandsmapping aanvragen, worden ze beperkt door het RAM-geheugen en de paginabestandsgrootte van het systeem. Zoals opgemerkt in de opmerking van Néstor Sánchez (Néstor Sánchez) op de blog, houdt de beheerde code zoals C # vast aan de begrenzing van het RAM- / paginabestand en de adresruimte van het besturingssysteem.


Dat was veel langer dan verwacht. Hopelijk helpt het iemand. Ik heb het gepost omdat ik de System.OutOfMemoryException een x64-programma uitvoeren op een systeem met 24 GB RAM, hoewel mijn array maar 2 GB aan spullen bevatte.


7
2017-12-24 23:15



Ik zou de / 3GB Windows opstartoptie afraden. Afgezien van al het andere (het is overdreven om dit voor te doen een slecht gedragen toepassing, en het zal uw probleem waarschijnlijk toch niet oplossen), het kan veel instabiliteit veroorzaken.

Veel Windows-stuurprogramma's worden niet getest met deze optie, dus een flink aantal van hen gaat ervan uit dat gebruikersmodusaanwijzers altijd naar de lagere 2 GB van de adresruimte verwijzen. Wat betekent dat ze vreselijk kunnen breken met / 3GB.

Windows beperkt echter normaalgesproken een 32-bits proces tot een adresruimte van 2 GB. Maar dat betekent niet dat u zou verwachten dat u 2 GB zou kunnen toewijzen!

De adresruimte is al bezaaid met allerlei toegewezen gegevens. Er is de stapel en alle verzamelingen die zijn geladen, statische variabelen enzovoort. Er is geen enkele garantie dat er 800MB aaneengesloten niet-toegewezen geheugen is.

Het is waarschijnlijk beter om 2 400 MB brokken toe te wijzen. Of 4 brokken van 200MB. Kleinere toewijzingen zijn veel gemakkelijker om ruimte voor te vinden in een gefragmenteerde geheugenruimte.

Hoe dan ook, als je dit op een 12-GB machine gaat inzetten, wil je dit als een 64-bit applicatie gebruiken, die alle problemen zou moeten oplossen.


5
2017-07-20 14:05



Veranderen van 32 naar 64 bit werkte voor mij - het proberen waard als je op een 64 bit pc zit en deze niet hoeft te poorten.


3
2017-08-31 21:04



Als je zulke grote structuren nodig hebt, kun je misschien Memory Mapped Files gebruiken. Dit artikel kan nuttig zijn: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

LP, Dejan


2
2017-07-20 14:09