"System.OutOfMemoryException ' został wyrzucony, gdy jest jeszcze dużo wolnej pamięci

To jest mój kod:

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

Wyjątek: Exception of type " System.OutOfMemoryException " został wyrzucony.

Mam 4GB pamięci na tym komputerze 2.5 GB jest wolny Kiedy zaczynam to uruchamianie, jest wyraźnie wystarczająco dużo miejsca na komputerze, aby obsłużyć 762mb losowych liczb 100000000. Muszę przechowywać jak najwięcej losowych liczb, biorąc pod uwagę dostępną pamięć. Kiedy przejdę do produkcji, na pudełku będzie 12GB i chcę z niego skorzystać.

Czy CLR ogranicza mnie do domyślnej pamięci maksymalnej na początek? jak Mogę prosić o więcej?

Update

Myślałem, że podzielenie tego na mniejsze kawałki i stopniowe dodawanie do moich wymagań pamięci pomoże, jeśli problem jest spowodowany fragmentacją pamięci , ale to nie nie mogę ominąć całkowitego rozmiaru ArrayList 256MB, niezależnie od tego, co robię, poprawiając rozmiar bloku .

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

Z mojej głównej metody:

int blockSize = 1000000;

while (true) {
  try
  {
    AddNDRandomNumbers(blockSize);                    
  }
  catch (System.OutOfMemoryException ex)
  {
    break;
  }
}            
double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0;
Author: John Saunders, 2009-07-20

14 answers

Możesz przeczytać to:"" Out of Memory" nie odnosi się do fizycznej pamięci " Erica Lipperta.

Krótko mówiąc, bardzo uproszczone, "Out of memory" nie oznacza, że ilość dostępnej pamięci jest zbyt mała. Najczęstszym powodem jest to, że w bieżącej przestrzeni adresowej nie ma przylegającej do siebie części pamięci, która byłaby wystarczająco duża, aby służyć żądanej alokacji. Jeśli masz 100 bloków, każdy o wielkości 4 MB, to nie pomoże Ci, gdy potrzebujesz jednego 5 MB blok.

Kluczowe Punkty:

  • Przechowywanie danych, które nazywamy "pamięcią procesową", jest moim zdaniem najlepiej wizualizowane jako ogromny plik na dysku.
  • PAMIĘĆ RAM może być postrzegana jedynie jako optymalizacja wydajności.]}
  • całkowita ilość pamięci wirtualnej zużywanej przez program nie ma większego znaczenia dla jego wydajności
  • "BRAK PAMIĘCI RAM" rzadko powoduje błąd "Brak pamięci". Zamiast błędu, skutkuje to złą wydajnością, ponieważ pełny koszt faktu, że pamięć faktycznie znajduje się na dysku nagle staje się istotna.
 145
Author: Fredrik Mörk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-15 03:04:36

Sprawdź, czy budujesz proces 64-bitowy, a nie 32-bitowy, który jest domyślnym trybem kompilacji programu Visual Studio. Aby to zrobić, kliknij prawym przyciskiem myszy swój projekt, Properties - > Build - > platform target : x64. Jak każdy 32-bitowy proces, aplikacje Visual Studio skompilowane w 32-bitach mają limit pamięci wirtualnej 2 GB.

64-bitowe procesy nie mają tego ograniczenia, ponieważ używają 64-bitowych wskaźników, więc ich teoretyczna maksymalna przestrzeń adresowa (rozmiar ich pamięci wirtualnej) wynosi 16 exabytes (2^64). W rzeczywistości Windows x64 ogranicza pamięć wirtualną procesów do 8 TB. Rozwiązaniem problemu ograniczenia pamięci jest skompilowanie w 64-bitowej wersji.

Rozmiar obiektu w Visual Studio jest domyślnie ograniczony do 2 GB. Będziesz mógł utworzyć kilka tablic, których łączny rozmiar będzie większy niż 2GB, ale domyślnie nie możesz tworzyć tablic większych niż 2GB. Mam nadzieję, że jeśli nadal chcesz tworzyć tablice większe niż 2GB, możesz to zrobić, dodając następujący kod do ciebie app.plik konfiguracyjny:

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>
 31
Author: Shift Technology,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-26 13:56:06

Nie masz ciągłego bloku pamięci w celu przydzielenia 762mb, twoja pamięć jest fragmentowana i alokator nie może znaleźć wystarczająco dużego otworu, aby przydzielić potrzebną pamięć.

  1. Możesz spróbować pracować z /3GB (jak sugerowali inni)
  2. lub przełączyć się na 64-bitowy system operacyjny.
  3. lub zmodyfikować algorytm, aby nie potrzebował dużej ilości pamięci. może przydzielić kilka mniejszych (względnie) kawałków pamięci.
 25
Author: Shay Erlichmen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 13:59:45

Jak zapewne się zorientowałeś, problem polega na tym, że próbujesz przydzielić jeden duży sąsiadujący blok pamięci, który nie działa z powodu fragmentacji pamięci. Gdybym potrzebował zrobić to, co robisz, zrobiłbym to:

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

Następnie, aby uzyskać konkretny indeks, użyjesz randomNumbers[i / sizeB][i % sizeB].

Inną opcją, jeśli zawsze masz dostęp do wartości w kolejności, może być użycie przeciążonego konstruktora do określenia ziarna. W ten sposób otrzymamy semi losową liczbę (jak na DateTime.Now.Ticks) przechowuj go w zmiennej, a gdy zaczniesz przeglądać listę, utworzysz nową instancję losową, używając oryginalnego zalążka:

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

Ważne jest, aby pamiętać, że podczas gdy blog linkowany w odpowiedzi Fredrika mörka wskazuje, że problem jest zwykle spowodowany brakiem przestrzeni adresowej , nie wymienia on wielu innych problemów, takich jak ograniczenie rozmiaru obiektu CLR 2GB (wspomniane w komentarzu od ShuggyCoUk na tym samym blogu), fragmentacji pamięci i nie wspomina o wpływie rozmiaru pliku strony (i o tym, jak można go rozwiązać za pomocą CreateFileMapping function).

Ograniczenie 2GB oznacza, że randomNumbers musi być mniejsze niż 2GB. Ponieważ tablice są klasami i mają narzut, oznacza to, że tablica double będzie musiała być mniejsza niż 2^31. Nie jestem pewien, o ile mniejsza niż 2^31 Długość musiałaby być, ale Overhead tablicy. NET? wskazuje 12 - 16 bajtów.

Fragmentacja pamięci jest bardzo podobna do fragmentacji dysku twardego. Możesz mieć 2 GB przestrzeni adresowej, ale podczas tworzenia i niszczenia obiektów pojawią się luki między wartościami. Jeśli luki te są zbyt małe dla dużego obiektu, a dodatkowe miejsce nie może być wymagane, otrzymasz System.OutOfMemoryException. Na przykład, jeśli utworzysz 2 miliony 1024-bajtowych obiektów, użyjesz 1,9 GB. Jeśli usuniesz każdy obiekt, w którym adres nie jest wielokrotnością 3, będziesz go używać .6GB pamięci, ale będzie ona rozłożona w przestrzeni adresowej z blokami otwartymi 2024 bajtów pomiędzy nimi. Jeśli trzeba utworzyć obiekt, który był .2GB nie byłoby w stanie tego zrobić, ponieważ nie ma bloku wystarczająco dużego, aby go zmieścić i nie można uzyskać dodatkowej przestrzeni (zakładając środowisko 32 bitowe). Możliwe rozwiązania tego problemu to na przykład użycie mniejszych obiektów, zmniejszenie ilości danych przechowywanych w pamięci lub użycie algorytmu zarządzania pamięcią w celu ograniczenia / zapobiegania pamięci fragmentacja. Należy zauważyć, że jeśli nie opracowujesz dużego programu, który wykorzystuje dużą ilość pamięci, nie będzie to problemem. Ponadto ten problem może pojawić się w systemach 64-bitowych, ponieważ system windows jest ograniczony głównie przez Rozmiar Pliku strony i ilość pamięci RAM w systemie.

Ponieważ większość programów żąda pamięci roboczej z systemu operacyjnego i nie żąda mapowania plików, będą one ograniczone przez pamięć RAM systemu i rozmiar pliku strony. Jak zaznaczono w komentarzu Néstora Sáncheza (Néstor Na blogu z zarządzanym kodem jak C# przyklejasz się do ograniczenia plików RAM/page i przestrzeni adresowej systemu operacyjnego.


To było o wiele dłuższe niż oczekiwano. Mam nadzieję, że to komuś pomoże. Opublikowałem go, ponieważ wpadłem na System.OutOfMemoryException uruchamiający program x64 na systemie z 24GB pamięci RAM, mimo że moja tablica miała tylko 2GB rzeczy.
 8
Author: Trisped,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:11

Odradzam opcję / 3GB Windows boot. Poza wszystkim innym (to przesada zrobić to dla jednej źle zachowującej się aplikacji, a to pewnie i tak nie rozwiąże Twojego problemu), może spowodować dużą niestabilność.

Wiele sterowników Windows nie jest testowanych z tą opcją, więc sporo z nich zakłada, że wskaźniki trybu użytkownika zawsze wskazują na niższe 2GB przestrzeni adresowej. Co oznacza, że mogą się strasznie zepsuć z / 3GB.

Jednak Windows robi normalnie ogranicz proces 32-bitowy do 2 GB przestrzeni adresowej. Ale to nie znaczy, że powinieneś spodziewać się możliwości przydzielenia 2GB!

Przestrzeń adresowa jest już zaśmiecona różnego rodzaju przydzielonymi danymi. Jest stos i wszystkie załadowane zespoły, zmienne statyczne i tak dalej. Nie ma gwarancji, że w dowolnym miejscu będzie 800MB sąsiedniej nieprzydzielonej pamięci.

Przydzielenie 2 400MB kawałków byłoby chyba lepsze. Lub 4 kawałki 200MB. Mniejsze przydziały są znacznie łatwiejsze do znajdź miejsce na fragmentaryczną przestrzeń pamięci.

W każdym razie, jeśli i tak zamierzasz wdrożyć to na maszynie 12GB, będziesz chciał uruchomić to jako 64-bitową aplikację, która powinna rozwiązać wszystkie problemy.

 5
Author: jalf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 14:05:46

Zmiana z 32 NA 64 bit zadziałała dla mnie-warto spróbować, jeśli jesteś na 64 bitowym komputerze i nie musisz portować.

 4
Author: chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-31 21:04:27

Jeśli potrzebujesz tak dużych struktur, być może mógłbyś wykorzystać pliki mapowane pamięcią. Ten artykuł może okazać się pomocny: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx

, Dejan

 2
Author: Dejan Stanič,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 14:09:09

32-bitowy windows ma limit pamięci procesowej 2GB. Opcja rozruchu / 3GB, o której wspominali inni, sprawi, że ten 3GB pozostanie tylko 1gb do użytku z jądrem systemu operacyjnego. Realistycznie, jeśli chcesz używać więcej niż 2GB bez kłopotów, wymagany jest 64-bitowy system operacyjny. To również rozwiązuje problem, w którym chociaż możesz mieć 4 GB fizycznej pamięci RAM, przestrzeń adresowa wymagana dla karty graficznej może sprawić, że spory uchwyt tej pamięci będzie bezużyteczny - zwykle około 500 MB.

 1
Author: redcalx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 14:03:07

Zamiast przydzielać ogromną tablicę, czy mógłbyś spróbować użyć iteratora? Są one wykonywane z opóźnieniem, co oznacza, że wartości są generowane tylko tak, jak są wymagane w instrukcji foreach; nie powinno zabraknąć pamięci w ten sposób:

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

Powyższe wygeneruje tyle losowych liczb, ile chcesz, ale wygeneruje je tylko tak, jak są wymagane za pomocą instrukcji foreach. W ten sposób nie zabraknie Ci pamięci.

Alternatywnie, jeśli musisz mieć je wszystkie w jednym miejscu, przechowuj je w plik zamiast w pamięci.

 1
Author: Judah Gabriel Himango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 17:05:39

Cóż, mam podobny problem z dużym zestawem danych i próba zmuszenia aplikacji do użycia tak dużej ilości danych nie jest naprawdę właściwą opcją. Najlepszą wskazówką, jaką mogę ci dać, jest przetwarzanie danych w małych kawałkach, jeśli jest to możliwe. Ponieważ radzenie sobie z tak dużą ilością danych, problem prędzej czy później powróci. Ponadto nie możesz znać konfiguracji każdego komputera, na którym będzie uruchamiana Twoja aplikacja, więc zawsze istnieje ryzyko, że wyjątek pojawi się na innym komputerze.

 0
Author: Francis B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 14:05:36

Miałem podobny problem, to było spowodowane StringBuilder.ToString ();

 0
Author: Ricardo Rix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-30 10:57:24

Przekonwertuj swoje rozwiązanie na x64. Jeśli nadal napotykasz problem, przyznaj maksymalną długość wszystkim, co powoduje wyjątek, jak poniżej:

 var jsSerializer = new JavaScriptSerializer();
 jsSerializer.MaxJsonLength = Int32.MaxValue;
 0
Author: Samidjo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-13 13:58:06

Jeśli nie potrzebujesz procesu hostingu Visual Studio:

Usuń zaznaczenie opcji: Project->Properties->Debug - > Enable the Visual Studio Hosting Process

A następnie budować.

Jeśli nadal masz problem:

Przejdź do linii poleceń Project->Properties->Build Events->post-Build Event i wklej następujący tekst:

call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86
"$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)"  /LARGEADDRESSAWARE
Teraz zbuduj projekt.
 0
Author: Yasir Arafat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-22 04:47:27

Zwiększ limit procesu Windows do 3gb. (poprzez boot.ini lub Vista Boot manager)

 -2
Author: leppie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-20 13:53:33