Maximaler Speicher, der dynamisch und zur Kompilierzeit in C ++ zugewiesen werden kann

8

Ich spiele herum, um zu verstehen, wie viel Speicher zugewiesen werden kann. Anfangs dachte ich, dass der maximale Speicher, der zugewiesen werden kann, gleich dem physikalischen Speicher (RAM) ist. Ich habe meinen Arbeitsspeicher unter Ubuntu 12.04 überprüft, indem ich den folgenden Befehl ausgeführt habe:

%Vor%

Wie oben gezeigt, ist der gesamte physische Speicher 3Gig (3170848768 Bytes), von denen nur 644108288 Bytes frei sind, also nahm ich an, dass ich nur soviel Speicher reservieren kann. Ich habe es getestet, indem ich das kleine Programm mit nur zwei Zeilen geschrieben habe:

%Vor%

Da der Code einwandfrei lief, bedeutet dies, dass er Speicher erfolgreich zugewiesen hat. Auch ich habe versucht, den Speicher größer als der verfügbare physische freie Speicher noch zuzuweisen, es hat keinen Fehler geworfen. Dann pro Frage

maximaler Speicher, den malloc zuordnen kann

Ich dachte, es muss den virtuellen Speicher verwenden. Also habe ich den Code für freien Tauschspeicher getestet und es hat auch funktioniert.

%Vor%

Ich habe versucht, den freien Austausch plus freie RAM-Bytes des Speichers zuzuordnen

%Vor%

Aber dieser Zeitcode konnte die bad_alloc Ausnahme nicht ausgeben. Warum hat der Code dieses Mal nicht funktioniert?

Jetzt habe ich den Speicher zur Kompilierzeit in einem neuen Programm wie folgt zugewiesen:

%Vor%

Der Ausgang zeigt

%Vor%

Könnten Sie mir bitte helfen zu verstehen, was hier passiert? Warum konnte der Speicher zur Kompilierzeit zugewiesen werden, aber nicht dynamisch? Wenn die Kompilierzeit zugewiesen wurde, konnten p und p1 mehr Speicher als Swap plus freien RAM-Speicher reservieren. Wo als p2 fehlgeschlagen ist. Wie genau funktioniert das? Ist dies ein undefiniertes Verhalten oder osspezifisches Verhalten? Danke für Ihre Hilfe. Ich benutze Ubuntu 12.04 und gcc 4.6.3.

    
CppLearner 25.10.2012, 18:16
quelle

5 Antworten

5

Speicherseiten werden erst dann Ihrem Programm zugeordnet, wenn Sie sie verwenden. All malloc does reserviert einen Bereich des virtuellen Adressraums. Diesen virtuellen Seiten wird kein physischer RAM zugeordnet, bis Sie versuchen, sie zu lesen oder zu schreiben.

Auch wenn Sie globalen Speicher oder Stapelspeicher ("automatisch") zuweisen, werden physische Seiten erst zugeordnet, wenn Sie sie berühren.

Schließlich wird sizeof() zur Kompilierzeit ausgewertet, wenn der Compiler keine Ahnung hat, was das OS später tun wird. So wird es nur die erwartete Größe des Objekts sagen.

Sie werden feststellen, dass sich die Dinge sehr unterschiedlich verhalten, wenn Sie versuchen, memset den Speicher in jedem Fall auf 0 zu setzen. Vielleicht möchten Sie auch calloc ausprobieren, wodurch der Speicher gelöscht wird.

    
Mike DeSimone 25.10.2012, 18:19
quelle
2

Interessant .... eine Sache zu beachten: wenn Sie

schreiben %Vor%

Sie reservieren (gut, reservieren) 100 Bytes auf dem Stapel.

Wenn Sie

schreiben %Vor%

Sie ordnen 100 Bytes auf dem Heap zu. Großer Unterschied. Jetzt weiß ich nicht, warum die Stapelzuordnungen funktionieren - es sei denn, der Wert zwischen [] wird vom Compiler als int gelesen und umschließt damit einen viel kleineren Block.

Die meisten Betriebssysteme weisen keinen physischen Speicher zu, sie geben Seiten aus einem virtuellen Adressraum, der nicht verwendet wird (und daher nicht zugewiesen ist), bis Sie sie verwenden, dann wird die Speicher-Manager-Einheit der CPU eingreifen die Erinnerung, nach der du gefragt hast. Versuchen Sie, auf die Bytes zu schreiben, die Sie zugewiesen haben, und sehen Sie, was passiert.

Auch unter Windows, wenn Sie einen Speicherblock zuweisen, können Sie nur den größten zusammenhängenden Block reservieren, den das Betriebssystem zur Verfügung hat. Wenn also der Speicher durch wiederholte Zuteilungen fragmentiert wird, reduziert sich der größte Seitenblock, den Sie malloc hinzufügen können. Ich weiß nicht, ob Linux auch dieses Problem hat.

    
gbjbaanb 25.10.2012 18:24
quelle
2

Es gibt einen großen Unterschied zwischen diesen beiden Programmen:

program1.cpp

%Vor%

program2.cpp:

%Vor%

Der erste weist Speicher auf dem Stapel zu; Es wird einen Segmentierungsfehler aufgrund eines Stapelüberlaufs erhalten. Die zweite macht überhaupt nicht viel. Diese Erinnerung existiert noch nicht ganz. Es ist in Form von Datensegmenten, die nicht berührt werden. Lassen Sie uns das zweite Programm so ändern, dass die Daten berührt werden:

%Vor%

Dies reserviert keinen Speicher für p1 , p2 oder p3 auf dem Heap oder dem Stack. Dieser Speicher lebt in Datensegmenten. Es ist ein Teil der Anwendung selbst. Es gibt ein großes Problem damit: Auf meinem Rechner wird diese Version nicht einmal verlinken.

    
David Hammen 25.10.2012 19:00
quelle
1

Das erste, was zu beachten ist, ist, dass in modernen Computern Prozesse keinen direkten Zugriff auf RAM bekommen (auf Anwendungsebene). Stattdessen wird das Betriebssystem jedem Prozess einen "virtuellen Adressraum" bereitstellen. Das Betriebssystem fängt Anrufe ab, um auf den virtuellen Speicher zuzugreifen und reserviert bei Bedarf den realen Speicher.

Wenn also malloc oder new angibt, genug Speicher für Sie gefunden zu haben, bedeutet das nur, dass Sie genug Speicherplatz für Sie im virtuellen Adressraum gefunden haben. Sie können dies überprüfen, indem Sie das folgende Programm mit der Zeile memset ausführen und es auskommentiert. (Vorsicht, dieses Programm verwendet eine Busy-Schleife).

%Vor%

Wenn memset Teil des Programms ist, werden Sie bemerken, dass der von Ihrem Computer verwendete Speicher massiv springt, und ohne sie sollten Sie kaum einen Unterschied bemerken, wenn überhaupt. Wenn memset aufgerufen wird, werden alle Elemente des Arrays aufgerufen, wodurch das Betriebssystem gezwungen wird, den verfügbaren Platz im physischen Speicher freizugeben. Da das Argument für neu ist ein size_t (siehe hier ) dann das maximale Argument, mit dem Sie es nennen können, ist 2^32-1 , obwohl das nicht garantiert ist, um erfolgreich zu sein (es tut sicher nicht auf meiner Maschine).

Was Ihre Stapelzuweisungen betrifft: David Hammems Antwort sagt es besser als ich könnte. Ich bin überrascht, dass Sie diese Programme kompilieren konnten. Mit dem gleichen Setup wie du (Ubuntu 12.04 und gcc 4.6) bekomme ich Kompilierfehler wie:

  

test.cpp: In der Funktion 'int main (int, char **)':

     

test.cpp: 14: 6: Fehler: Größe der Variablen 'arr' ist zu groß

    
Dunes 25.10.2012 19:54
quelle
0

probiere den folgenden Code:

%Vor%

Öffnen Sie in einem Durchgang keine anderen Programme, in dem anderen Lauf laden Sie einige große Dateien in einer Anwendung, die Speicherabbilddateien verwenden.

Dies kann Ihnen helfen, das zu verstehen.

    
marscode 25.10.2012 18:47
quelle