Jak zapewne się domyślasz, problem polega na tym, że próbujesz przydzielić jeden duży, ciągły blok pamięci, który nie działa z powodu fragmentacji pamięci. Gdybym musiał zrobić to, co robisz, zrobiłbym następujące:
int sizeA = 10000,
sizeB = 10000;
double sizeInMegabytes = (sizeA * sizeB * 8.0) / 1024.0 / 1024.0;
double[][] randomNumbers = new double[sizeA][];
for (int i = 0; i < randomNumbers.Length; i++)
{
randomNumbers[i] = new double[sizeB];
}
Następnie, aby uzyskać określony indeks, którego użyjesz randomNumbers[i / sizeB][i % sizeB]
.
Inną opcją, jeśli zawsze uzyskujesz dostęp do wartości w kolejności, może być użycie przeciążonego konstruktora do określenia ziarna. W ten sposób otrzymujesz półlosową liczbę (taką jak DateTime.Now.Ticks
), przechowuj ją w zmiennej, a następnie kiedykolwiek zaczniesz przeglądać listę, utworzysz nową instancję Random, używając oryginalnego ziarna:
private static int randSeed = (int)DateTime.Now.Ticks;
private static Random GetNewRandomIterator()
{
return new Random(randSeed);
}
Należy zauważyć, że chociaż blog, do którego link znajduje się w odpowiedzi Fredrika Mörka, wskazuje, że problem jest zwykle spowodowany brakiem przestrzeni adresowej , nie zawiera on wielu innych problemów, takich jak ograniczenie rozmiaru obiektu CLR do 2 GB (wspomniane w komentarzu z ShuggyCoUk na tym samym blogu), omawia fragmentację pamięci i nie wspomina o wpływie rozmiaru pliku strony (i o tym, jak można go rozwiązać za pomocą CreateFileMapping
funkcji ).
Ograniczenie 2 GB oznacza, że randomNumbers
musi być mniej niż 2 GB. Ponieważ tablice są klasami i mają same narzuty, oznacza to, że tablica double
będzie musiała być mniejsza niż 2 ^ 31. Nie jestem pewien, o ile mniejsza musiałaby być długość 2 ^ 31, ale narzut macierzy .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 będą luki między wartościami. Jeśli te luki są zbyt małe dla dużego obiektu i nie można zażądać dodatkowej przestrzeni, otrzymasz plikSystem.OutOfMemoryException
. Na przykład, jeśli utworzysz 2 miliony obiektów 1024-bajtowych, używasz 1,9 GB. Jeśli usuniesz każdy obiekt, którego adres nie jest wielokrotnością 3, użyjesz 0,6 GB pamięci, ale zostanie ona rozłożona w przestrzeni adresowej z 2024-bajtowymi otwartymi blokami pomiędzy nimi. Jeśli potrzebujesz stworzyć obiekt o wielkości .2GB, nie będziesz 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 / zapobieżenia fragmentacji pamięci. Należy zauważyć, że jeśli nie tworzysz dużego programu, który używa dużej ilości pamięci, nie będzie to problemem. Również,
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 stronicowania. Jak zauważono w komentarzu Néstora Sáncheza (Néstor Sánchez) na blogu, z kodem zarządzanym, takim jak C #, utkniesz w ograniczeniu pamięci RAM / pliku stronicowania i przestrzeni adresowej systemu operacyjnego.
To było o wiele dłużej niż oczekiwano. Mam nadzieję, że to komuś pomoże. Opublikowałem to, ponieważ natknąłem się na System.OutOfMemoryException
uruchamianie programu x64 w systemie z 24 GB pamięci RAM, mimo że moja tablica zawierała tylko 2 GB.