Wie Sie wahrscheinlich herausgefunden haben, besteht das Problem darin, dass Sie versuchen, einen großen zusammenhängenden Speicherblock zuzuweisen, der aufgrund der Speicherfragmentierung nicht funktioniert. Wenn ich tun müsste, was Sie tun, würde ich Folgendes tun:
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];
}
Dann würden Sie einen bestimmten Index erhalten, den Sie verwenden würden randomNumbers[i / sizeB][i % sizeB]
.
Eine andere Option, wenn Sie immer der Reihe nach auf die Werte zugreifen, besteht darin, den Startwert mit dem überladenen Konstruktor anzugeben. Auf diese Weise erhalten Sie eine Halbzufallszahl (wie die DateTime.Now.Ticks
), die in einer Variablen gespeichert ist. Wenn Sie dann die Liste durchgehen, erstellen Sie eine neue Zufallsinstanz mit dem ursprünglichen Startwert:
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 ist wichtig zu beachten, dass der in Fredrik Mörks Antwort verlinkte Blog zwar darauf hinweist, dass das Problem normalerweise auf einen Mangel an Adressraum zurückzuführen ist , jedoch keine Reihe anderer Probleme auflistet, wie z. B. die Beschränkung der CLR-Objektgröße auf 2 GB (in einem Kommentar von erwähnt) ShuggyCoUk im selben Blog) beschönigt die Speicherfragmentierung und erwähnt nicht die Auswirkungen der Auslagerungsdateigröße (und wie sie mithilfe der CreateFileMapping
Funktion behoben werden kann ).
Die Beschränkung auf 2 GB bedeutet, randomNumbers
dass weniger als 2 GB vorhanden sein müssen. Da Arrays Klassen sind und selbst einen gewissen Overhead haben, bedeutet dies, dass ein Array von double
kleiner als 2 ^ 31 sein muss. Ich bin nicht sicher, wie viel kleiner als 2 ^ 31 die Länge sein müsste, aber Overhead eines .NET-Arrays? zeigt 12 - 16 Bytes an.
Die Speicherfragmentierung ist der Festplattenfragmentierung sehr ähnlich. Möglicherweise verfügen Sie über 2 GB Adressraum. Beim Erstellen und Zerstören von Objekten treten jedoch Lücken zwischen den Werten auf. Wenn diese Lücken für Ihr großes Objekt zu klein sind und kein zusätzlicher Speicherplatz angefordert werden kann, erhalten Sie dieSystem.OutOfMemoryException
. Wenn Sie beispielsweise 2 Millionen 1024-Byte-Objekte erstellen, verwenden Sie 1,9 GB. Wenn Sie jedes Objekt löschen, bei dem die Adresse kein Vielfaches von 3 ist, verwenden Sie 0,6 GB Speicher, der jedoch über den Adressraum verteilt ist und dazwischen offene Blöcke mit 2024 Byte enthält. Wenn Sie ein Objekt mit einer Größe von 0,2 GB erstellen müssen, können Sie dies nicht tun, da kein Block vorhanden ist, der groß genug ist, um ihn einzufügen, und kein zusätzlicher Speicherplatz verfügbar ist (unter der Annahme einer 32-Bit-Umgebung). Mögliche Lösungen für dieses Problem sind beispielsweise die Verwendung kleinerer Objekte, die Reduzierung der im Speicher gespeicherten Datenmenge oder die Verwendung eines Speicherverwaltungsalgorithmus zur Begrenzung / Verhinderung der Speicherfragmentierung. Es ist zu beachten, dass dies kein Problem darstellt, es sei denn, Sie entwickeln ein großes Programm, das viel Speicher benötigt. Ebenfalls,
Da die meisten Programme Arbeitsspeicher vom Betriebssystem anfordern und keine Dateizuordnung anfordern, werden sie durch den RAM und die Auslagerungsdateigröße des Systems begrenzt. Wie im Kommentar von Néstor Sánchez (Néstor Sánchez) im Blog erwähnt, bleiben Sie bei verwaltetem Code wie C # bei der Beschränkung der RAM- / Auslagerungsdatei und des Adressraums des Betriebssystems.
Das war viel länger als erwartet. Hoffentlich hilft es jemandem. Ich habe es gepostet, weil ich System.OutOfMemoryException
auf einem System mit 24 GB RAM auf das Ausführen eines x64-Programms gestoßen bin, obwohl mein Array nur 2 GB Material enthielt.