Kurz gesagt
Ein Stack wird für die statische Speicherzuweisung und ein Heap für die dynamische Speicherzuweisung verwendet, die beide im RAM des Computers gespeichert sind.
Im Detail
Der Stack
Der Stack ist eine "LIFO" (last in, first out) Datenstruktur, die vom CPU sehr genau verwaltet und optimiert wird. Jedes Mal, wenn eine Funktion eine neue Variable deklariert, wird sie auf den Stack "geschoben". Dann werden jedes Mal, wenn eine Funktion beendet wird, alle Variablen, die von dieser Funktion auf den Stack geschoben wurden, freigegeben (das heißt, sie werden gelöscht). Sobald eine Stack-Variable freigegeben ist, wird dieser Speicherbereich für andere Stack-Variablen verfügbar.
Der Vorteil der Verwendung des Stacks zur Speicherung von Variablen besteht darin, dass der Speicher für Sie verwaltet wird. Sie müssen keinen Speicher manuell zuweisen oder freigeben, wenn Sie ihn nicht mehr benötigen. Darüber hinaus, da der CPU den Stackspeicher so effizient organisiert, ist das Lesen von und Schreiben auf Stack-Variablen sehr schnell.
Weitere Informationen finden Sie hier.
Der Heap
Der Heap ist ein Bereich des Speichers Ihres Computers, der nicht automatisch für Sie verwaltet wird und nicht so eng vom CPU verwaltet wird. Es handelt sich um einen freischwebenden Speicherbereich (und ist größer). Um Speicher im Heap zuzuweisen, müssen Sie malloc() oder calloc() verwenden, die eingebaute C-Funktionen sind. Sobald Sie Speicher im Heap zugewiesen haben, sind Sie dafür verantwortlich, diesen Speicher mit free() freizugeben, sobald Sie ihn nicht mehr benötigen.
Wenn Sie dies nicht tun, wird Ihr Programm einen sogenannten Speicherleck haben. Das bedeutet, dass Speicher im Heap weiterhin reserviert wird (und anderen Prozessen nicht zur Verfügung steht). Wie wir im Abschnitt zur Fehlersuche sehen werden, gibt es ein Tool namens Valgrind, das Ihnen helfen kann, Speicherlecks zu erkennen.
Im Gegensatz zum Stack gibt es auf dem Heap keine Größenbeschränkungen für die Variablengröße (abgesehen von den offensichtlichen physikalischen Grenzen Ihres Computers). Heap-Speicher ist etwas langsamer zu lesen und zu schreiben, da man Zeiger verwenden muss, um auf den Speicher im Heap zuzugreifen. Über Zeiger werden wir gleich sprechen.
Im Gegensatz zum Stack sind auf dem Heap erstellte Variablen von jeder Funktion, überall in Ihrem Programm zugänglich. Heap-Variablen haben im Wesentlichen einen globalen Geltungsbereich.
Weitere Informationen finden Sie hier.
Auf dem Stack allokierte Variablen werden direkt im Speicher gespeichert, und der Zugriff auf diesen Speicher ist sehr schnell. Der Allocator wird während der Programmkompilierung behandelt. Wenn eine Funktion oder eine Methode eine andere Funktion aufruft, die wiederum eine andere Funktion aufruft usw., bleibt die Ausführung all dieser Funktionen ausgesetzt, bis die allerletzte Funktion ihren Wert zurückgibt. Der Stack ist immer in LIFO-Reihenfolge reserviert, der zuletzt reservierte Block ist immer der nächste Block, der freigegeben wird. Es ist also sehr einfach, den Stack zu verwalten, das Freigeben eines Blocks vom Stack bedeutet nichts anderes als das Anpassen eines Zeigers.
Auf dem Heap allokierte Variablen haben ihren Speicher zur Laufzeit zugewiesen, und der Zugriff auf diesen Speicher ist etwas langsamer, aber die Heap-Größe ist nur durch die Größe des virtuellen Speichers begrenzt. Elemente des Heaps haben keine Abhängigkeiten voneinander und können jederzeit beliebig zufällig zugegriffen werden. Sie können zu jedem Zeitpunkt einen Block zuweisen und ihn zu jedem Zeitpunkt freigeben. Daher ist es viel komplizierter, den Überblick darüber zu behalten, welche Teile des Heaps zu einem bestimmten Zeitpunkt belegt oder freigegeben sind.
Sie können den Stack verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierungszeit zuweisen müssen und sie nicht zu groß sind. Sie können den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie zur Laufzeit benötigen werden oder wenn Sie eine große Menge an Daten zuweisen müssen.
In einer mehrfädigen Situation hat jeder Thread seinen eigenen vollständig unabhängigen Stack, aber sie teilen sich den Heap. Der Stack ist fadenspezifisch, und der Heap ist anwendungsbezogen. Der Stack ist wichtig bei Ausnahmebehandlungen und Threadausführungen.
Jeder Thread erhält einen Stack, während es typischerweise nur einen Heap für die Anwendung gibt (obwohl es nicht ungewöhnlich ist, mehrere Heaps für verschiedene Arten von Zuweisungen zu haben).
Zur Laufzeit, wenn die Anwendung mehr Heap benötigt, kann sie Speicher aus freiem Speicher zuweisen, und wenn der Stack Speicher benötigt, kann er Speicher aus freiem Speicher zuweisen, der für die Anwendung allokiert wurde.
Noch weitere Details finden Sie hier und hier.
Kommen Sie nun zu den Antworten Ihrer Fragen.
In welchem Umfang werden sie vom Betriebssystem oder der Sprachlaufzeitkontrolle beeinflusst?
Das Betriebssystem reserviert den Stack für jeden Systemthread auf Systemebene, wenn der Thread erstellt wird. Typischerweise ruft die Sprachlaufzeit das Betriebssystem auf, um den Heap für die Anwendung zuzuweisen.
Weitere Informationen finden Sie hier.
Was ist ihr Geltungsbereich?
Bereits oben angegeben.
"Sie können den Stack verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierungszeit zuweisen müssen, und es nicht zu groß ist. Sie können den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie zur Laufzeit benötigen oder wenn Sie eine große Menge an Daten zuweisen müssen."
Es gibt weitere Informationen hier.
Was bestimmt ihre Größe?
Die Größe des Stacks wird vom Betriebssystem festgelegt, wenn ein Thread erstellt wird. Die Größe des Heaps wird beim Start der Anwendung festgelegt, kann jedoch bei Bedarf wachsen (der Allocator fordert mehr Speicher vom Betriebssystem an).
Was macht einen davon schneller?
Die Stackzuweisung ist viel schneller, da alles, was es wirklich tut, das Verschieben des Stack-Zeigers ist. Durch die Verwendung von Speicherpools können Sie eine vergleichbare Leistung aus der Heapzuweisung erhalten, aber dies bringt eine leichte zusätzliche Komplexität und eigene Kopfschmerzen mit sich.
Außerdem ist Stapel gegenüber Heap nicht nur eine Überlegung in Bezug auf die Leistung; es sagt Ihnen auch viel über die erwartete Lebensdauer von Objekten aus.
Weitere Details finden Sie hier.
5 Stimmen
@mattshane Die Definitionen von Stack und Heap hängen überhaupt nicht von Wert- und Referenztypen ab. Mit anderen Worten, der Stack und Heap können vollständig definiert werden, auch wenn Wert- und Referenztypen niemals existiert hätten. Darüber hinaus ist der Stack nur ein Implementierungsdetail beim Verständnis von Wert- und Referenztypen. Laut Eric Lippert: Der Stack ist ein Implementierungsdetail, Teil Eins.
248 Stimmen
Eine wirklich gute Erklärung finden Sie hier Was ist der Unterschied zwischen einem Stapel und einem Heap?
19 Stimmen
Auch (wirklich) gut: codeproject.com/Articles/76153/… (der Stack-/Heap-Teil)
21 Stimmen
youtube.com/watch?v=clOUdVDDzIM&spfreload=5
5 Stimmen
Verwandte, siehe Stack Clash. Die Stack Clash-Beseitigungen betrafen einige Aspekte von Systemvariablen und Verhaltensweisen wie
rlimit_stack
. Siehe auch Red Hat Problem 14632411 Stimmen
Nicht klar in den Antworten: Für eine Laufzeitumgebung für eine Programmiersprache (z.B. .NET) gibt es pro Thread einen Stack, um Methodenaufrufe/lokale Variablen zu verwalten, und nur einen gemeinsamen Heap für alle Prozesse der Laufzeitumgebung. Der Heap wird vom Garbage Collector überwacht. Die Speicherbereiche der Laufzeitumgebung (Stacks/Heap) sind Teil des zusammenhängenden virtuellen Speichers, der vom Betriebssystem den Prozessen zugewiesen wird (der selbst von physischen RAM-Blöcken in keiner bestimmten Reihenfolge versorgt wird), auf Anforderung der Prozesse. Die Verwirrung um "Stacks" liegt an der Existenz vieler "Stack"-Arten in einem Computer, die nicht mit den Laufzeitumgebungs-Stacks zusammenhängen. Ein "Stack" ist einfach eine LIFO-Speicherstruktur.
0 Stimmen
Wenn Sie eine Simulation sehen möchten, wie der Stapel und der Heap während der Ausführung eines C-Programms aussehen, versuchen Sie C Tutor.
0 Stimmen
Du solltest definitiv dieses Video sehen, das alle oben genannten Antworten klärt youtube.com/watch?v=7O4JPdKjc30