Wenn Sie Speicher auf dem Heap zuweisen, ist die einzige Grenze der freie RAM (oder der virtuelle Speicher). Es macht Gb des Gedächtnisses.
Warum ist die Stapelgröße so begrenzt (ca. 1 MB)? Welcher technische Grund hindert Sie daran, wirklich große Objekte auf dem Stapel zu erstellen?
Update : Meine Absicht ist möglicherweise nicht klar. Ich möchte keine großen Objekte auf dem Stapel zuweisen und benötige keinen größeren Stapel. Diese Frage ist nur reine Neugier.
c++
memory-management
undu
quelle
quelle
Antworten:
Meine Intuition ist die folgende. Der Stapel ist nicht so einfach zu verwalten wie der Heap. Der Stapel muss an fortlaufenden Speicherorten gespeichert werden. Dies bedeutet, dass Sie den Stapel nicht nach Bedarf zufällig zuordnen können, sondern mindestens virtuelle Adressen für diesen Zweck reservieren müssen. Je größer der reservierte virtuelle Adressraum ist, desto weniger Threads können Sie erstellen.
Beispielsweise hat eine 32-Bit-Anwendung im Allgemeinen einen virtuellen Adressraum von 2 GB. Dies bedeutet, dass Sie bei einer Stapelgröße von 2 MB (standardmäßig in pthreads) maximal 1024 Threads erstellen können. Dies kann für Anwendungen wie Webserver klein sein. Durch Erhöhen der Stapelgröße auf beispielsweise 100 MB (dh Sie reservieren 100 MB, weisen dem Stapel jedoch nicht unbedingt sofort 100 MB zu) würde die Anzahl der Threads auf etwa 20 begrenzen, was selbst für einfache GUI-Anwendungen einschränkend sein kann.
Eine interessante Frage ist, warum wir dieses Limit auf 64-Bit-Plattformen immer noch haben. Ich kenne die Antwort nicht, gehe aber davon aus, dass die Benutzer bereits an einige "Best Practices für Stapel" gewöhnt sind: Achten Sie darauf, große Objekte auf dem Heap zuzuweisen und die Stapelgröße bei Bedarf manuell zu erhöhen. Daher fand es niemand nützlich, auf 64-Bit-Plattformen "riesige" Stack-Unterstützung hinzuzufügen.
quelle
Ein Aspekt, den noch niemand erwähnt hat:
Eine begrenzte Stapelgröße ist ein Fehlererkennungs- und Eindämmungsmechanismus.
Im Allgemeinen besteht die Hauptaufgabe des Stapels in C und C ++ darin, den Aufrufstapel und die lokalen Variablen zu verfolgen. Wenn der Stapel außerhalb der Grenzen wächst, ist dies fast immer ein Fehler im Design und / oder im Verhalten der Anwendung .
Wenn der Stapel beliebig groß werden könnte, würden diese Fehler (wie die unendliche Rekursion) erst sehr spät abgefangen, wenn die Ressourcen des Betriebssystems erschöpft sind. Dies wird verhindert, indem eine beliebige Grenze für die Stapelgröße festgelegt wird. Die tatsächliche Größe ist nicht so wichtig, abgesehen davon, dass sie klein genug ist, um eine Systemverschlechterung zu verhindern.
quelle
std::unique_ptr
, um einen Destruktor zu schreiben (und sich nicht auf den intelligenten Zeiger zu verlassen)).Es ist nur eine Standardgröße. Wenn Sie mehr benötigen, können Sie mehr erhalten - meistens, indem Sie den Linker anweisen, zusätzlichen Stapelspeicher zuzuweisen.
Der Nachteil großer Stapel besteht darin, dass beim Erstellen vieler Threads jeweils ein Stapel benötigt wird. Wenn alle Stapel Multi-MBs zuweisen, diese jedoch nicht verwenden, wird der Speicherplatz verschwendet.
Sie müssen die richtige Balance für Ihr Programm finden.
Einige Leute, wie @BJovke, glauben, dass der virtuelle Speicher im Wesentlichen frei ist. Es ist wahr, dass Sie keinen physischen Speicher benötigen, der den gesamten virtuellen Speicher sichert. Sie müssen in der Lage sein, mindestens Adressen an den virtuellen Speicher zu vergeben.
Auf einem typischen 32-Bit-PC entspricht die Größe des virtuellen Speichers jedoch der Größe des physischen Speichers, da für jede virtuelle oder nicht virtuelle Adresse nur 32 Bit zur Verfügung stehen.
Da alle Threads in einem Prozess denselben Adressraum verwenden, müssen sie ihn zwischen ihnen aufteilen. Und nachdem das Betriebssystem seinen Teil dazu beigetragen hat, bleiben "nur" 2-3 GB für eine Anwendung übrig. Und diese Größe ist die Grenze sowohl für den physischen als auch für den virtuellen Speicher, da es einfach keine Adressen mehr gibt.
quelle
Zum einen ist der Stapel kontinuierlich. Wenn Sie also 12 MB zuweisen, müssen Sie 12 MB entfernen, wenn Sie die von Ihnen erstellten Werte unterschreiten möchten. Auch das Bewegen von Objekten wird viel schwieriger. Hier ist ein Beispiel aus der Praxis, das das Verständnis erleichtern kann:
Angenommen, Sie stapeln Kisten um einen Raum. Welches ist einfacher zu verwalten:
Diese beiden Beispiele sind grobe Verallgemeinerungen, und es gibt einige Punkte, die in der Analogie offensichtlich falsch sind, aber sie sind nah genug, dass sie Ihnen hoffentlich helfen werden, die Vorteile in beiden Fällen zu erkennen.
quelle
Denken Sie an den Stapel in der Reihenfolge von nah bis fern. Die Register befinden sich in der Nähe der CPU (schnell), der Stapel ist etwas weiter entfernt (aber immer noch relativ nah) und der Heap ist weit entfernt (langsamer Zugriff).
Der Stack lebt natürlich auf dem Heap, aber da er kontinuierlich verwendet wird, verlässt er wahrscheinlich nie die CPU-Cache (s) und ist damit schneller als nur der durchschnittliche Heap-Zugriff. Dies ist ein Grund, die Größe des Stapels angemessen zu halten. um es so weit wie möglich zwischengespeichert zu halten. Das Zuweisen großer Stapelobjekte (möglicherweise wird die Größe des Stapels automatisch geändert, wenn Überläufe auftreten) verstößt gegen dieses Prinzip.
Es ist also ein gutes Paradigma für die Leistung, nicht nur ein Überbleibsel aus alten Zeiten.
quelle
Viele der Dinge, für die Sie einen großen Stapel benötigen, können auf andere Weise erledigt werden.
Sedgewicks "Algorithmen" enthält einige gute Beispiele für das "Entfernen" der Rekursion aus rekursiven Algorithmen wie QuickSort, indem die Rekursion durch Iteration ersetzt wird. In Wirklichkeit ist der Algorithmus immer noch rekursiv und es gibt immer noch einen Stapel, aber Sie weisen den Sortierstapel auf dem Heap zu, anstatt den Laufzeitstapel zu verwenden.
(Ich bevorzuge die zweite Ausgabe mit Algorithmen, die in Pascal angegeben sind. Sie kann für acht Dollar verwendet werden.)
Eine andere Sichtweise ist, wenn Sie glauben, einen großen Stapel zu benötigen, ist Ihr Code ineffizient. Es gibt einen besseren Weg, der weniger Stapel verwendet.
quelle
Ich glaube nicht, dass es einen technischen Grund gibt, aber es wäre eine seltsame App, die nur ein riesiges Superobjekt auf dem Stapel erstellt hat. Stapelobjekte haben keine Flexibilität, die mit zunehmender Größe problematischer wird. Sie können nicht zurückkehren, ohne sie zu zerstören, und Sie können sie nicht in andere Threads einreihen.
quelle
int main() { char buffer[1048576]; }
Es ist ein sehr häufiges Problem für Neulinge. Sicher gibt es eine einfache Problemumgehung, aber warum sollten wir die Stapelgröße umgehen müssen?