Das ist mein Code:
int size = 100000000;
double sizeInMegabytes = (size * 8.0) / 1024.0 / 1024.0; //762 mb
double[] randomNumbers = new double[size];
Ausnahme: Ausnahme vom Typ 'System.OutOfMemoryException' wurde ausgelöst.
Ich habe 4 GB Speicher auf diesem Computer. 2,5 GB sind frei, wenn ich dies starte. Auf dem PC ist eindeutig genug Speicherplatz vorhanden, um die 762 MB mit 100000000 Zufallszahlen zu verarbeiten. Ich muss so viele Zufallszahlen wie möglich speichern, wenn Speicher verfügbar ist. Wenn ich zur Produktion gehe, sind 12 GB auf der Box und ich möchte davon Gebrauch machen.
Beschränkt mich die CLR zunächst auf einen Standard-Maximalspeicher? und wie fordere ich mehr an?
Aktualisieren
Ich dachte, dies in kleinere Teile aufzuteilen und meine Speicheranforderungen schrittweise zu erhöhen, würde helfen, wenn das Problem auf Speicherfragmentierung zurückzuführen ist , aber ich komme nicht über eine ArrayList-Gesamtgröße von 256 MB hinaus, unabhängig davon, was ich bei der Optimierung von blockSize mache .
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()));
}
}
Von meiner Hauptmethode:
int blockSize = 1000000;
while (true) {
try
{
AddNDRandomNumbers(blockSize);
}
catch (System.OutOfMemoryException ex)
{
break;
}
}
double arrayTotalSizeInMegabytes = (ndRandomNumbers.Count * 8.0) / 1024.0 / 1024.0;
quelle
Antworten:
Vielleicht möchten Sie Folgendes lesen: " " Nicht genügend Speicher "bezieht sich nicht auf den physischen Speicher " von Eric Lippert.
Kurz gesagt und sehr vereinfacht bedeutet "Nicht genügend Speicher" nicht wirklich, dass die Menge des verfügbaren Speichers zu gering ist. Der häufigste Grund ist, dass im aktuellen Adressraum kein zusammenhängender Teil des Speichers vorhanden ist, der groß genug ist, um die gewünschte Zuordnung zu erfüllen. Wenn Sie 100 Blöcke mit jeweils 4 MB haben, hilft Ihnen das nicht, wenn Sie einen 5-MB-Block benötigen.
Wichtige Punkte:
quelle
Stellen Sie sicher, dass Sie einen 64-Bit-Prozess erstellen und keinen 32-Bit-Prozess. Dies ist der Standardkompilierungsmodus von Visual Studio. Klicken Sie dazu mit der rechten Maustaste auf Ihr Projekt, Eigenschaften -> Erstellen -> Plattformziel: x64. Wie bei jedem 32-Bit-Prozess haben in 32-Bit kompilierte Visual Studio-Anwendungen ein virtuelles Speicherlimit von 2 GB.
64-Bit-Prozesse unterliegen dieser Einschränkung nicht, da sie 64-Bit-Zeiger verwenden. Daher beträgt ihr theoretischer maximaler Adressraum (die Größe ihres virtuellen Speichers) 16 Exabyte (2 ^ 64). In Wirklichkeit beschränkt Windows x64 den virtuellen Speicher von Prozessen auf 8 TB. Die Lösung für das Problem der Speicherbeschränkung besteht dann darin, in 64-Bit zu kompilieren.
Die Objektgröße in Visual Studio ist jedoch standardmäßig weiterhin auf 2 GB beschränkt. Sie können mehrere Arrays erstellen, deren kombinierte Größe größer als 2 GB ist. Sie können jedoch standardmäßig keine Arrays erstellen, die größer als 2 GB sind. Wenn Sie dennoch Arrays mit mehr als 2 GB erstellen möchten, können Sie dies hoffentlich tun, indem Sie der Datei app.config den folgenden Code hinzufügen:
quelle
Sie haben keinen kontinuierlichen Speicherblock, um 762 MB zuzuweisen, Ihr Speicher ist fragmentiert und der Allokator kann keine ausreichend große Lücke finden, um den benötigten Speicher zuzuweisen.
quelle
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:
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: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 vondouble
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 die
System.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.quelle
Ich würde von der Windows-Boot-Option / 3GB abraten. Abgesehen von allem anderen (es ist übertrieben, dies für eine schlecht benommene Anwendung zu tun , und es wird Ihr Problem wahrscheinlich sowieso nicht lösen), kann es eine Menge Instabilität verursachen.
Viele Windows-Treiber werden mit dieser Option nicht getestet, daher gehen einige davon aus, dass Zeiger im Benutzermodus immer auf die unteren 2 GB des Adressraums verweisen. Was bedeutet, dass sie mit / 3GB schrecklich brechen können.
Normalerweise beschränkt Windows einen 32-Bit-Prozess jedoch auf einen Adressraum von 2 GB. Das heißt aber nicht, dass Sie damit rechnen sollten, 2 GB zuweisen zu können!
Der Adressraum ist bereits mit allen Arten von zugewiesenen Daten übersät. Es gibt den Stapel und alle geladenen Assemblys, statische Variablen und so weiter. Es gibt keine Garantie dafür, dass irgendwo 800 MB zusammenhängender nicht zugewiesener Speicher vorhanden sind.
Das Zuweisen von 2 400-MB-Blöcken wäre wahrscheinlich besser. Oder 4 200 MB große Stücke. Kleinere Zuordnungen sind in einem fragmentierten Speicherbereich viel einfacher zu finden.
Wenn Sie dies ohnehin auf einem 12-GB-Computer bereitstellen möchten, sollten Sie dies als 64-Bit-Anwendung ausführen, um alle Probleme zu lösen.
quelle
Der Wechsel von 32 auf 64 Bit hat bei mir funktioniert - einen Versuch wert, wenn Sie auf einem 64-Bit-PC arbeiten und dieser nicht portiert werden muss.
quelle
Wenn Sie so große Strukturen benötigen, können Sie möglicherweise Speicherzuordnungsdateien verwenden. Dieser Artikel könnte sich als hilfreich erweisen: http://www.codeproject.com/KB/recipes/MemoryMappedGenericArray.aspx
LP, Dejan
quelle
32-Bit-Fenster haben eine Prozessspeicherbeschränkung von 2 GB. Die von anderen erwähnte / 3GB-Startoption macht diese 3 GB mit nur noch 1 GB für die Verwendung des Betriebssystemkerns. Realistisch gesehen ist ein 64-Bit-Betriebssystem erforderlich, wenn Sie problemlos mehr als 2 GB verwenden möchten. Dies überwindet auch das Problem, dass der für die Grafikkarte erforderliche Adressraum, obwohl Sie möglicherweise über 4 GB physischen RAM verfügen, einen beträchtlichen Speicherplatz unbrauchbar machen kann - normalerweise etwa 500 MB.
quelle
Könnten Sie versuchen, einen Iterator zu verwenden, anstatt ein massives Array zuzuweisen? Diese werden verzögert ausgeführt, dh Werte werden nur generiert, wenn sie in einer foreach-Anweisung angefordert werden. Auf diese Weise sollte Ihnen nicht der Speicher ausgehen:
Das oben Genannte generiert so viele Zufallszahlen, wie Sie möchten, generiert sie jedoch nur so, wie sie über eine foreach-Anweisung angefordert werden. Auf diese Weise wird Ihnen nicht der Speicher ausgehen.
Alternativ: Wenn Sie alle an einem Ort haben müssen, speichern Sie sie in einer Datei und nicht im Speicher.
quelle
Nun, ich habe ein ähnliches Problem mit großen Datenmengen und der Versuch, die Anwendung zu zwingen, so viele Daten zu verwenden, ist nicht wirklich die richtige Option. Der beste Tipp, den ich Ihnen geben kann, ist, Ihre Daten in kleinen Stücken zu verarbeiten, wenn dies möglich ist. Da es sich um so viele Daten handelt, wird das Problem früher oder später wieder auftreten. Außerdem können Sie nicht die Konfiguration jedes Computers kennen, auf dem Ihre Anwendung ausgeführt wird, sodass immer das Risiko besteht, dass die Ausnahme auf einem anderen PC auftritt.
quelle
Ich hatte ein ähnliches Problem, es lag an einem StringBuilder.ToString ();
quelle
Konvertieren Sie Ihre Lösung in x64. Wenn Sie immer noch auf ein Problem stoßen, gewähren Sie allen, die eine Ausnahme wie die folgende auslösen, die maximale Länge:
quelle
Wenn Sie den Visual Studio-Hostingprozess nicht benötigen:
Deaktivieren Sie die Option: Projekt-> Eigenschaften-> Debug-> Aktivieren Sie den Visual Studio-Hosting-Prozess
Und dann bauen.
Wenn Sie immer noch vor dem Problem stehen:
Gehen Sie zu Projekt-> Eigenschaften-> Ereignisse erstellen-> Ereignisereignis nach dem Erstellen und fügen Sie Folgendes ein:
Erstellen Sie nun das Projekt.
quelle
Erhöhen Sie das Windows-Prozesslimit auf 3 GB. (über boot.ini oder Vista Boot Manager)
quelle