In Programmiersprachenbüchern wird erklärt, dass Werttypen auf dem Stapel und Referenztypen auf dem Heap erstellt werden , ohne zu erklären, was diese beiden Dinge sind. Ich habe keine klare Erklärung dafür gelesen. Ich verstehe, was ein Stapel ist. Aber,
- Wo und was sind sie (physisch im Speicher eines echten Computers)?
- Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
- Was ist ihr Umfang?
- Was bestimmt die Größe jedes einzelnen von ihnen?
- Was macht einen schneller?
rlimit_stack
. Siehe auch Red Hat Issue 1463241Antworten:
Der Stapel ist der Speicher, der als Arbeitsbereich für einen Ausführungsthread reserviert ist. Wenn eine Funktion aufgerufen wird, wird oben im Stapel ein Block für lokale Variablen und einige Buchhaltungsdaten reserviert. Wenn diese Funktion zurückkehrt, wird der Block nicht mehr verwendet und kann beim nächsten Aufruf einer Funktion verwendet werden. Der Stapel wird immer in einer LIFO-Reihenfolge (last in first out) reserviert. Der zuletzt reservierte Block ist immer der nächste freizugebende Block. Dies macht es wirklich einfach, den Stapel im Auge zu behalten. Das Befreien eines Blocks vom Stapel ist nichts anderes als das Anpassen eines Zeigers.
Der Heap ist Speicher, der für die dynamische Zuordnung reserviert ist. Im Gegensatz zum Stapel gibt es kein erzwungenes Muster für die Zuweisung und Freigabe von Blöcken aus dem Heap. Sie können einen Block jederzeit zuweisen und jederzeit freigeben. Dies macht es viel komplexer, zu verfolgen, welche Teile des Heaps zu einem bestimmten Zeitpunkt zugewiesen oder frei sind. Es stehen viele benutzerdefinierte Heap-Allokatoren zur Verfügung, um die Heap-Leistung für verschiedene Verwendungsmuster zu optimieren.
Jeder Thread erhält einen Stapel, während es normalerweise nur einen Heap für die Anwendung gibt (obwohl es nicht ungewöhnlich ist, mehrere Heaps für verschiedene Zuordnungstypen zu haben).
So beantworten Sie Ihre Fragen direkt:
Das Betriebssystem weist den Stapel jedem Thread auf Systemebene zu, wenn der Thread erstellt wird. In der Regel wird das Betriebssystem von der Sprachlaufzeit aufgerufen, um den Heap für die Anwendung zuzuweisen.
Der Stapel ist an einen Thread angehängt. Wenn der Thread beendet wird, wird der Stapel zurückgefordert. Der Heap wird normalerweise beim Start der Anwendung zur Laufzeit zugewiesen und beim Beenden der Anwendung (technisch verarbeitet) zurückgefordert.
Die Größe des Stapels wird festgelegt, wenn ein Thread erstellt wird. Die Größe des Heapspeichers wird beim Start der Anwendung festgelegt, kann jedoch bei Bedarf an Speicherplatz zunehmen (der Allokator fordert mehr Speicher vom Betriebssystem an).
Der Stapel ist schneller, da das Zugriffsmuster das Zuweisen und Freigeben von Speicher von ihm trivial macht (ein Zeiger / eine Ganzzahl wird einfach inkrementiert oder dekrementiert), während der Heap eine viel komplexere Buchhaltung bei einer Zuweisung oder Freigabe aufweist. Außerdem wird jedes Byte im Stapel sehr häufig wiederverwendet, was bedeutet, dass es dem Cache des Prozessors zugeordnet wird, was es sehr schnell macht. Ein weiterer Leistungseinbruch für den Heap besteht darin, dass der Heap, der hauptsächlich eine globale Ressource ist, in der Regel multithreading-sicher sein muss, dh jede Zuweisung und Freigabe muss - normalerweise - mit "allen" anderen Heap-Zugriffen im Programm synchronisiert werden.
Eine klare Demonstration:
Bildquelle: vikashazrati.wordpress.com
quelle
Stapel:
Haufen:
delete
,delete[]
oderfree
.new
bzw. zugeordnetmalloc
.Beispiel:
quelle
C
Sprache, wie sie imC99
Sprachstandard definiert ist (verfügbar unter open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf ), einen "Stapel" erfordert. Tatsächlich kommt das Wort "Stapel" nicht einmal im Standard vor. Dies beantwortet Aussagen zur Stapelverwendung von wrt / toC
, die im Allgemeinen wahr sind, aber in keiner Weise von der Sprache verlangt werden. Weitere Informationen finden Sie unter knosof.co.uk/cbook/cbook.html und insbesondereC
in der Implementierung auf Odd-Ball-Architekturen wie en.wikipedia.org/wiki/Burroughs_large_systemsDer wichtigste Punkt ist, dass Heap und Stack allgemeine Begriffe für die Zuweisung von Speicher sind. Sie können auf viele verschiedene Arten implementiert werden, und die Begriffe gelten für die Grundkonzepte.
In einem Stapel von Gegenständen liegen die Gegenstände in der Reihenfolge, in der sie dort platziert wurden, übereinander, und Sie können nur den obersten entfernen (ohne das Ganze umzustürzen).
Die Einfachheit eines Stapels besteht darin, dass Sie keine Tabelle verwalten müssen, die einen Datensatz für jeden Abschnitt des zugewiesenen Speichers enthält. Die einzige Statusinformation, die Sie benötigen, ist ein einzelner Zeiger auf das Ende des Stapels. Zum Zuweisen und Aufheben der Zuweisung erhöhen und verringern Sie einfach diesen einzelnen Zeiger. Hinweis: Manchmal kann ein Stapel implementiert werden, der am oberen Rand eines Speicherabschnitts beginnt und sich nach unten erstreckt, anstatt nach oben zu wachsen.
In einem Haufen gibt es keine bestimmte Reihenfolge für die Platzierung von Elementen. Sie können Artikel in beliebiger Reihenfolge erreichen und entfernen, da es keinen eindeutigen "Top" -Element gibt.
Die Heap-Zuweisung erfordert die vollständige Aufzeichnung, welcher Speicher zugewiesen ist und was nicht, sowie einige Overhead-Wartungsarbeiten, um die Fragmentierung zu verringern, zusammenhängende Speichersegmente zu finden, die groß genug sind, um der angeforderten Größe zu entsprechen, und so weiter. Der Speicher kann jederzeit freigegeben werden, sodass freier Speicherplatz verbleibt. Manchmal führt ein Speicherzuweiser Wartungsaufgaben aus, z. B. das Defragmentieren des Speichers durch Verschieben des zugewiesenen Speichers oder das Sammeln von Speicherplatz. Er identifiziert zur Laufzeit, wenn der Speicher nicht mehr im Umfang ist, und gibt die Zuordnung auf.
Diese Bilder sollten die beiden Möglichkeiten zum Zuweisen und Freigeben von Speicher in einem Stapel und einem Heap recht gut beschreiben. Yum!
Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
Wie bereits erwähnt, sind Heap und Stack allgemeine Begriffe und können auf viele Arten implementiert werden. Computerprogramme haben normalerweise einen Stapel, der als Aufrufstapel bezeichnet wird und Informationen speichert, die für die aktuelle Funktion relevant sind, wie z. B. einen Zeiger auf die Funktion, von der aus sie aufgerufen wurden, und lokale Variablen. Da Funktionen andere Funktionen aufrufen und dann zurückkehren, wächst und schrumpft der Stapel, um Informationen von den Funktionen weiter unten im Aufrufstapel zu speichern. Ein Programm hat nicht wirklich die Laufzeitsteuerung. Es wird von der Programmiersprache, dem Betriebssystem und sogar der Systemarchitektur bestimmt.
Ein Heap ist ein allgemeiner Begriff für jeden Speicher, der dynamisch und zufällig zugewiesen wird. dh außer Betrieb. Der Speicher wird normalerweise vom Betriebssystem zugewiesen, wobei die Anwendung API-Funktionen aufruft, um diese Zuordnung vorzunehmen. Die Verwaltung des dynamisch zugewiesenen Speichers erfordert einiges an Overhead, das normalerweise vom Laufzeitcode der verwendeten Programmiersprache oder -umgebung verwaltet wird.
Was ist ihr Umfang?
Der Aufrufstapel ist ein so niedriges Konzept, dass er sich nicht auf "Umfang" im Sinne der Programmierung bezieht. Wenn Sie Code zerlegen, werden relative Verweise auf Zeigerstile auf Teile des Stapels angezeigt. In Bezug auf eine übergeordnete Sprache legt die Sprache jedoch ihre eigenen Bereichsregeln fest. Ein wichtiger Aspekt eines Stapels ist jedoch, dass nach der Rückkehr einer Funktion alles, was für diese Funktion lokal ist, sofort vom Stapel freigegeben wird. Das funktioniert so, wie Sie es erwarten würden, wenn man bedenkt, wie Ihre Programmiersprachen funktionieren. In einem Haufen ist es auch schwierig zu definieren. Der Bereich ist das, was auch immer vom Betriebssystem verfügbar gemacht wird, aber Ihre Programmiersprache fügt wahrscheinlich die Regeln hinzu, was ein "Bereich" in Ihrer Anwendung ist. Die Prozessorarchitektur und das Betriebssystem verwenden die virtuelle Adressierung. was der Prozessor in physische Adressen übersetzt und es gibt Seitenfehler usw. Sie verfolgen, welche Seiten zu welchen Anwendungen gehören. Sie müssen sich darüber jedoch nie wirklich Gedanken machen, da Sie nur die Methode verwenden, die Ihre Programmiersprache verwendet, um Speicher zuzuweisen und freizugeben, und nach Fehlern suchen (wenn die Zuweisung / Freigabe aus irgendeinem Grund fehlschlägt).
Was bestimmt die Größe jedes einzelnen von ihnen?
Auch dies hängt von der Sprache, dem Compiler, dem Betriebssystem und der Architektur ab. Ein Stapel ist normalerweise vorab zugewiesen, da er per Definition ein zusammenhängender Speicher sein muss. Der Sprachcompiler oder das Betriebssystem bestimmen seine Größe. Sie speichern keine großen Datenmengen auf dem Stapel, daher ist er groß genug, um niemals vollständig verwendet zu werden, außer in Fällen unerwünschter endloser Rekursion (daher "Stapelüberlauf") oder anderer ungewöhnlicher Programmierentscheidungen.
Ein Heap ist ein allgemeiner Begriff für alles, was dynamisch zugewiesen werden kann. Je nachdem, wie Sie es betrachten, ändert sich die Größe ständig. In modernen Prozessoren und Betriebssystemen ist die genaue Funktionsweise ohnehin sehr abstrahiert, sodass Sie sich normalerweise keine Gedanken darüber machen müssen, wie es tief im Inneren funktioniert, außer dass Sie (in Sprachen, in denen Sie dies zulassen) keinen Speicher verwenden dürfen Sie haben noch keinen Speicher zugewiesen, den Sie freigegeben haben.
Was macht einen schneller?
Der Stapel ist schneller, da der gesamte freie Speicher immer zusammenhängend ist. Es muss keine Liste aller Segmente des freien Speichers geführt werden, nur ein einziger Zeiger auf den aktuellen oberen Rand des Stapels. Compiler speichern diesen Zeiger normalerweise zu diesem Zweck in einem speziellen, schnellen Register . Darüber hinaus konzentrieren sich nachfolgende Operationen auf einem Stapel normalerweise auf sehr nahe gelegene Speicherbereiche, was auf einer sehr niedrigen Ebene für die Optimierung durch die On-Die-Caches des Prozessors gut ist.
quelle
(Ich habe diese Antwort von einer anderen Frage verschoben, die mehr oder weniger ein Betrüger dieser Frage war.)
Die Antwort auf Ihre Frage ist implementierungsspezifisch und kann je nach Compiler und Prozessorarchitektur variieren. Hier ist jedoch eine vereinfachte Erklärung.
Der Haufen
new
odermalloc
) werden erfüllt, indem aus einem der freien Blöcke ein geeigneter Block erstellt wird. Dies erfordert die Aktualisierung der Liste der Blöcke auf dem Heap. Diese Metainformationen zu den Blöcken auf dem Heap werden auch häufig in einem kleinen Bereich direkt vor jedem Block auf dem Heap gespeichert.Der Stapel
Nein, Aktivierungsdatensätze für Funktionen (dh lokale oder automatische Variablen) werden auf dem Stapel zugewiesen, der nicht nur zum Speichern dieser Variablen, sondern auch zum Verfolgen verschachtelter Funktionsaufrufe verwendet wird.
Wie der Heap verwaltet wird, hängt wirklich von der Laufzeitumgebung ab. C verwendet
malloc
und C ++ verwendetnew
, aber viele andere Sprachen haben Garbage Collection.Der Stapel ist jedoch ein Merkmal auf niedrigerer Ebene, das eng mit der Prozessorarchitektur verbunden ist. Das Wachsen des Heaps, wenn nicht genügend Speicherplatz vorhanden ist, ist nicht allzu schwierig, da es in dem Bibliotheksaufruf implementiert werden kann, der den Heap verarbeitet. Das Wachsen des Stapels ist jedoch häufig nicht möglich, da der Stapelüberlauf nur dann erkannt wird, wenn es zu spät ist. Das Herunterfahren des Ausführungsthreads ist die einzig praktikable Option.
quelle
Im folgenden C # -Code
So wird der Speicher verwaltet
Local Variables
Das muss nur so lange dauern, wie der Funktionsaufruf im Stapel abläuft. Der Heap wird für Variablen verwendet, deren Lebensdauer wir nicht wirklich kennen, aber wir erwarten, dass sie eine Weile dauern. In den meisten Sprachen ist es wichtig, dass wir zur Kompilierungszeit wissen, wie groß eine Variable ist, wenn wir sie auf dem Stapel speichern möchten.Objekte (deren Größe beim Aktualisieren variiert) werden auf dem Heap gespeichert, da wir zum Zeitpunkt der Erstellung nicht wissen, wie lange sie dauern werden. In vielen Sprachen wird der Heap durch Müll gesammelt, um Objekte (wie das Objekt cls1) zu finden, die keine Referenzen mehr haben.
In Java werden die meisten Objekte direkt in den Heap verschoben. In Sprachen wie C / C ++ können Strukturen und Klassen häufig auf dem Stapel verbleiben, wenn Sie nicht mit Zeigern arbeiten.
Weitere Informationen finden Sie hier:
Der Unterschied zwischen Stapel- und Heapspeicherzuordnung «timmurphy.org
und hier:
Erstellen von Objekten auf dem Stapel und dem Heap
Dieser Artikel ist die Quelle des obigen Bildes: Sechs wichtige .NET-Konzepte: Stapel, Heap, Werttypen, Referenztypen, Boxen und Unboxing - CodeProject
Beachten Sie jedoch, dass es einige Ungenauigkeiten enthalten kann.
quelle
Der Stapel Wenn Sie eine Funktion aufrufen, werden die Argumente für diese Funktion sowie ein anderer Overhead auf den Stapel gelegt. Dort werden auch einige Informationen gespeichert (z. B. wohin Sie bei der Rückgabe gehen müssen). Wenn Sie eine Variable in Ihrer Funktion deklarieren, wird diese Variable auch dem Stapel zugewiesen.
Die Freigabe des Stapels ist ziemlich einfach, da Sie die Zuordnung immer in der umgekehrten Reihenfolge aufheben, in der Sie sie zuweisen. Beim Eingeben von Funktionen werden Stapelmaterial hinzugefügt, die entsprechenden Daten werden beim Beenden entfernt. Dies bedeutet, dass Sie dazu neigen, in einem kleinen Bereich des Stapels zu bleiben, es sei denn, Sie rufen viele Funktionen auf, die viele andere Funktionen aufrufen (oder erstellen eine rekursive Lösung).
Der Heap Der Heap ist ein generischer Name für den Ort, an dem Sie die von Ihnen erstellten Daten im laufenden Betrieb ablegen. Wenn Sie nicht wissen, wie viele Raumschiffe Ihr Programm erstellen wird, verwenden Sie wahrscheinlich den neuen (oder malloc oder einen gleichwertigen) Operator, um jedes Raumschiff zu erstellen. Diese Zuordnung wird noch eine Weile bestehen bleiben, daher werden wir die Dinge wahrscheinlich in einer anderen Reihenfolge freigeben, als wir sie erstellt haben.
Daher ist der Heap weitaus komplexer, da es am Ende Speicherbereiche gibt, die nicht verwendet werden, verschachtelt mit Blöcken, die - Speicher wird fragmentiert. Es ist ein schwieriges Problem, freien Speicher in der Größe zu finden, die Sie benötigen. Aus diesem Grund sollte der Heap vermieden werden (obwohl er immer noch häufig verwendet wird).
Implementierung Die Implementierung von Stack und Heap hängt normalerweise von der Laufzeit / dem Betriebssystem ab. Oft erstellen Spiele und andere Anwendungen, die leistungskritisch sind, ihre eigenen Speicherlösungen, die einen großen Teil des Speichers aus dem Heap holen und ihn dann intern austeilen, um nicht auf das Betriebssystem als Speicher angewiesen zu sein.
Dies ist nur dann praktisch, wenn Ihre Speichernutzung stark von der Norm abweicht - dh für Spiele, bei denen Sie in einem großen Vorgang ein Level laden und in einem anderen großen Vorgang das ganze Los wegwerfen können.
Physischer Speicherort Dies ist weniger relevant als Sie denken, da eine Technologie namens Virtual Memory Ihr Programm den Eindruck erweckt, dass Sie Zugriff auf eine bestimmte Adresse haben, an der sich die physischen Daten an einem anderen Ort befinden (sogar auf der Festplatte!). Die Adressen, die Sie für den Stapel erhalten, werden in aufsteigender Reihenfolge angezeigt, wenn Ihr Aufrufbaum tiefer wird. Die Adressen für den Heap sind nicht vorhersehbar (dh implementierungsspezifisch) und offen gesagt nicht wichtig.
quelle
Zur Verdeutlichung hat diese Antwort falsche Informationen ( Thomas hat seine Antwort nach Kommentaren korrigiert , cool :)). Bei anderen Antworten wird nur vermieden, zu erklären, was statische Zuordnung bedeutet. Daher werde ich die drei Hauptformen der Zuordnung erläutern und wie sie sich normalerweise auf das Heap-, Stack- und Datensegment unten beziehen. Ich werde auch einige Beispiele in C / C ++ und Python zeigen, um den Leuten das Verständnis zu erleichtern.
"Statische" (statisch zugewiesene) AKA-Variablen werden dem Stapel nicht zugewiesen. Gehen Sie nicht davon aus - viele Leute tun dies nur, weil "statisch" sehr nach "Stapel" klingt. Sie existieren tatsächlich weder im Stapel noch im Haufen. Sie sind Teil des sogenannten Datensegments .
Im Allgemeinen ist es jedoch besser, " Umfang " und " Lebensdauer " als "Stapel" und "Heap" zu berücksichtigen .
Der Bereich bezieht sich darauf, welche Teile des Codes auf eine Variable zugreifen können. Im Allgemeinen denken wir an den lokalen Bereich (auf den nur mit der aktuellen Funktion zugegriffen werden kann) im Vergleich zum globalen Bereich (auf den überall zugegriffen werden kann), obwohl der Bereich viel komplexer werden kann.
Die Lebensdauer bezieht sich darauf, wann eine Variable während der Programmausführung zugewiesen und freigegeben wird. Normalerweise denken wir an eine statische Zuordnung (Variable bleibt während der gesamten Dauer des Programms erhalten, wodurch sie zum Speichern derselben Informationen über mehrere Funktionsaufrufe hinweg nützlich ist) im Vergleich zur automatischen Zuordnung (Variable bleibt nur während eines einzelnen Aufrufs einer Funktion bestehen, was sie nützlich macht Speichern von Informationen, die nur während Ihrer Funktion verwendet werden und nach Abschluss verworfen werden können, im Vergleich zur dynamischen Zuordnung (Variablen, deren Dauer zur Laufzeit definiert wird, anstelle der Kompilierungszeit wie statisch oder automatisch).
Obwohl die meisten Compiler und Interpreter dieses Verhalten in Bezug auf die Verwendung von Stacks, Heaps usw. ähnlich implementieren, kann ein Compiler manchmal gegen diese Konventionen verstoßen, wenn er dies wünscht, solange das Verhalten korrekt ist. Beispielsweise kann aufgrund der Optimierung eine lokale Variable nur in einem Register vorhanden sein oder vollständig entfernt werden, obwohl die meisten lokalen Variablen im Stapel vorhanden sind. Wie bereits in einigen Kommentaren erwähnt, können Sie einen Compiler implementieren, der nicht einmal einen Stack oder einen Heap verwendet, sondern einige andere Speichermechanismen (selten ausgeführt, da Stacks und Heaps dafür hervorragend geeignet sind).
Ich werde einen einfachen kommentierten C-Code bereitstellen, um all dies zu veranschaulichen. Der beste Weg zu lernen ist, ein Programm unter einem Debugger auszuführen und das Verhalten zu beobachten. Wenn Sie lieber Python lesen möchten, fahren Sie mit dem Ende der Antwort fort :)
Ein besonders ergreifendes Beispiel dafür, warum es wichtig ist, zwischen Lebensdauer und Gültigkeitsbereich zu unterscheiden, ist, dass eine Variable einen lokalen Gültigkeitsbereich, aber eine statische Lebensdauer haben kann - zum Beispiel "someLocalStaticVariable" im obigen Codebeispiel. Solche Variablen können unsere allgemeinen, aber informellen Namensgewohnheiten sehr verwirrend machen. Wenn wir beispielsweise " lokal " sagen, meinen wir normalerweise " automatisch zugewiesene Variable mit lokalem Gültigkeitsbereich " und wenn wir global sagen, meinen wir normalerweise " statisch zugewiesene Variable mit globalem Gültigkeitsbereich ". Leider sagen viele Leute, wenn es um Dinge wie " statisch zugewiesene Variablen mit Dateibereich " geht, nur ... " huh ??? ".
Einige der Syntaxoptionen in C / C ++ verschärfen dieses Problem - zum Beispiel denken viele Leute, dass globale Variablen aufgrund der unten gezeigten Syntax nicht "statisch" sind.
Beachten Sie, dass das Einfügen des Schlüsselworts "static" in die obige Deklaration verhindert, dass var2 einen globalen Gültigkeitsbereich hat. Trotzdem hat die globale var1 eine statische Zuordnung. Das ist nicht intuitiv! Aus diesem Grund versuche ich, bei der Beschreibung des Bereichs niemals das Wort "statisch" zu verwenden und stattdessen etwas wie "Datei" oder "Dateibegrenzter" Bereich zu sagen. Viele Benutzer verwenden jedoch den Ausdruck "statisch" oder "statischer Bereich", um eine Variable zu beschreiben, auf die nur über eine Codedatei zugegriffen werden kann. Im Kontext der Lebensdauer bedeutet "statisch" immer, dass die Variable beim Programmstart zugewiesen und beim Beenden des Programms freigegeben wird.
Einige Leute halten diese Konzepte für C / C ++ -spezifisch. Sie sind nicht. Das folgende Python-Beispiel zeigt beispielsweise alle drei Arten der Zuordnung (es gibt einige subtile Unterschiede bei interpretierten Sprachen, auf die ich hier nicht näher eingehen werde).
quelle
PostScript
haben mehrere Stapel, aber einen "Haufen", der sich eher wie ein Stapel verhält.Andere haben die breiten Striche ziemlich gut beantwortet, deshalb werde ich ein paar Details einbringen.
Stapel und Haufen müssen nicht singulär sein. Eine häufige Situation, in der Sie mehr als einen Stapel haben, ist, wenn Sie mehr als einen Thread in einem Prozess haben. In diesem Fall hat jeder Thread seinen eigenen Stapel. Sie können auch mehr als einen Heap haben. Einige DLL-Konfigurationen können beispielsweise dazu führen, dass unterschiedliche DLLs von unterschiedlichen Heaps zugewiesen werden. Daher ist es im Allgemeinen eine schlechte Idee, den von einer anderen Bibliothek zugewiesenen Speicher freizugeben.
In C können Sie den Vorteil einer Zuweisung mit variabler Länge durch die Verwendung von Zuweisung erhalten , die auf dem Stapel zugewiesen wird, im Gegensatz zu Zuweisung, die auf dem Heap zugewiesen wird. Dieser Speicher überlebt Ihre return-Anweisung nicht, ist jedoch für einen Arbeitspuffer nützlich.
Es ist nicht kostenlos, unter Windows einen riesigen temporären Puffer zu erstellen, von dem Sie nicht viel verwenden. Dies liegt daran, dass der Compiler eine Stapelprüfschleife generiert, die bei jeder Eingabe Ihrer Funktion aufgerufen wird, um sicherzustellen, dass der Stapel vorhanden ist (da Windows eine einzelne Schutzseite am Ende Ihres Stapels verwendet, um zu erkennen, wann der Stapel vergrößert werden muss. Wenn Sie mehr als eine Seite am Ende des Stapels auf den Speicher zugreifen, stürzen Sie ab. Beispiel:
quelle
alloca
?Andere haben Ihre Frage direkt beantwortet, aber wenn Sie versuchen, den Stapel und den Heap zu verstehen, halte ich es für hilfreich, das Speicherlayout eines herkömmlichen UNIX-Prozesses (ohne Threads und
mmap()
allokierte Allokatoren) zu berücksichtigen . Die Memory Management Glossar Web - Seite hat ein Diagramm dieses Speicherlayout.Der Stapel und der Heap befinden sich traditionell an entgegengesetzten Enden des virtuellen Adressraums des Prozesses. Der Stapel wächst beim Zugriff automatisch bis zu einer vom Kernel festgelegten Größe (mit der angepasst werden kann
setrlimit(RLIMIT_STACK, ...)
). Der Heap wächst, wenn der Speicherzuweiser den Systemaufrufbrk()
oder aufruftsbrk()
und mehr Seiten physischen Speichers dem virtuellen Adressraum des Prozesses zuordnet.In Systemen ohne virtuellen Speicher, wie z. B. einigen eingebetteten Systemen, gilt häufig das gleiche Grundlayout, außer dass der Stapel und der Heap eine feste Größe haben. In anderen eingebetteten Systemen (wie z. B. solchen, die auf Microchip PIC-Mikrocontrollern basieren) ist der Programmstapel jedoch ein separater Speicherblock, der nicht durch Datenbewegungsanweisungen adressiert werden kann und nur durch Programmflussanweisungen (Aufruf, Rückkehr usw.). Andere Architekturen, wie z. B. Intel Itanium-Prozessoren, haben mehrere Stapel . In diesem Sinne ist der Stack ein Element der CPU-Architektur.
quelle
Was ist ein Stapel?
Ein Stapel ist ein Stapel von Objekten, normalerweise einer, der ordentlich angeordnet ist.
Was ist ein Haufen?
Ein Haufen ist eine unordentliche Sammlung von Dingen, die sich willkürlich stapeln.
Beide zusammen
Was ist schneller - der Stapel oder der Haufen? Und warum?
Für Programmieranfänger ist es wahrscheinlich eine gute Idee, den Stack zu verwenden, da dies einfacher ist.
Da der Stapel klein ist, sollten Sie ihn verwenden, wenn Sie genau wissen, wie viel Speicher Sie für Ihre Daten benötigen, oder wenn Sie wissen, dass Ihre Daten sehr klein sind.
Es ist besser, den Heap zu verwenden, wenn Sie wissen, dass Sie viel Speicher für Ihre Daten benötigen oder einfach nicht sicher sind, wie viel Speicher Sie benötigen (wie bei einem dynamischen Array).
Java-Speichermodell
Der Stapel ist der Speicherbereich, in dem lokale Variablen (einschließlich Methodenparameter) gespeichert sind. Bei Objektvariablen handelt es sich lediglich um Verweise (Zeiger) auf die tatsächlichen Objekte auf dem Heap.
Jedes Mal, wenn ein Objekt instanziiert wird, wird ein Teil des Heapspeichers beiseite gelegt, um die Daten (den Status) dieses Objekts zu speichern. Da Objekte andere Objekte enthalten können, können einige dieser Daten tatsächlich Verweise auf diese verschachtelten Objekte enthalten.
quelle
Der Stapel ist ein Teil des Speichers, der über mehrere wichtige Anweisungen in Assemblersprache bearbeitet werden kann, z. B. 'pop' (Entfernen und Zurückgeben eines Werts vom Stapel) und 'push' (Verschieben eines Werts auf den Stapel), aber auch Aufrufen ( Rufen Sie eine Unterroutine auf - dies drückt die Adresse, um zum Stapel zurückzukehren) und kehren Sie zurück (Rückkehr von einer Unterroutine - dies entfernt die Adresse vom Stapel und springt dorthin). Dies ist der Speicherbereich unterhalb des Stapelzeigerregisters, der nach Bedarf eingestellt werden kann. Der Stapel wird auch zum Übergeben von Argumenten an Unterroutinen und zum Beibehalten der Werte in Registern vor dem Aufrufen von Unterroutinen verwendet.
Der Heap ist ein Teil des Speichers, der vom Betriebssystem an eine Anwendung übergeben wird, normalerweise über einen Systemaufruf wie malloc. Unter modernen Betriebssystemen besteht dieser Speicher aus einer Reihe von Seiten, auf die nur der aufrufende Prozess Zugriff hat.
Die Größe des Stapels wird zur Laufzeit festgelegt und wächst im Allgemeinen nach dem Start des Programms nicht mehr. In einem C-Programm muss der Stapel groß genug sein, um jede in jeder Funktion deklarierte Variable aufzunehmen. Der Heap wächst nach Bedarf dynamisch, aber das Betriebssystem führt letztendlich den Aufruf durch (häufig wird der Heap um mehr als den von malloc angeforderten Wert vergrößert, sodass zumindest einige zukünftige Mallocs nicht zum Kernel zurückkehren müssen Holen Sie sich mehr Speicher. Dieses Verhalten ist oft anpassbar.
Da Sie den Stapel vor dem Starten des Programms zugewiesen haben, müssen Sie nie mallocieren, bevor Sie den Stapel verwenden können. Dies ist dort also ein kleiner Vorteil. In der Praxis ist es sehr schwer vorherzusagen, was in modernen Betriebssystemen mit virtuellen Speichersubsystemen schnell und was langsam sein wird, da die Art und Weise, wie die Seiten implementiert und wo sie gespeichert werden, ein Implementierungsdetail ist.
quelle
Ich denke, viele andere Leute haben Ihnen in dieser Angelegenheit größtenteils richtige Antworten gegeben.
Ein Detail, das jedoch übersehen wurde, ist, dass der "Haufen" tatsächlich wahrscheinlich als "freier Laden" bezeichnet werden sollte. Der Grund für diese Unterscheidung ist, dass der ursprüngliche freie Speicher mit einer Datenstruktur implementiert wurde, die als "Binomialheap" bekannt ist. Aus diesem Grund war die Zuweisung aus frühen Implementierungen von malloc () / free () die Zuweisung von einem Haufen. In der heutigen Zeit werden die meisten freien Speicher jedoch mit sehr ausgefeilten Datenstrukturen implementiert, die keine Binomialhaufen sind.
quelle
C
. Dies ist ein weit verbreitetes Missverständnis, obwohl es das (bei weitem) dominierende Paradigma für die Implementierung vonC99 6.2.4 automatic storage duration objects
(Variablen) ist. In der Tat bedeutet das Wort „Stack“ erscheint nicht einmal in derC99
Sprache Standard: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdfMit dem Stack können Sie einige interessante Dinge tun. Zum Beispiel haben Sie Funktionen wie alloca (vorausgesetzt, Sie können die zahlreichen Warnungen bezüglich seiner Verwendung überwinden ), eine Form von malloc, die speziell den Stapel und nicht den Heap als Speicher verwendet.
Trotzdem sind stapelbasierte Speicherfehler einige der schlimmsten, die ich je erlebt habe. Wenn Sie Heapspeicher verwenden und die Grenzen Ihres zugewiesenen Blocks überschreiten, besteht eine gute Chance, dass ein Segmentfehler ausgelöst wird. (Nicht 100%: Ihr Block kann nebenbei mit einem anderen Block verknüpft sein, den Sie zuvor zugewiesen haben.) Da jedoch auf dem Stapel erstellte Variablen immer zusammenhängend sind, kann das Schreiben außerhalb der Grenzen den Wert einer anderen Variablen ändern. Ich habe gelernt, dass mein Programm, wenn ich das Gefühl habe, die Gesetze der Logik nicht mehr zu befolgen, wahrscheinlich ein Pufferüberlauf ist.
quelle
alloca
? Funktioniert es beispielsweise unter Windows? Ist es nur für Unix-ähnliche Betriebssysteme?Auf dem Stapel werden einfach lokale Variablen erstellt. Außerdem werden jedes Mal, wenn Sie eine Unterroutine aufrufen, der Programmzähler (Zeiger auf die nächste Maschinenanweisung) und alle wichtigen Register und manchmal die Parameter auf den Stapel verschoben. Dann werden alle lokalen Variablen innerhalb des Unterprogramms auf den Stapel verschoben (und von dort aus verwendet). Wenn die Unterroutine beendet ist, wird das Zeug wieder vom Stapel genommen. Die PC- und Registerdaten werden abgerufen und wieder dort abgelegt, wo sie sich gerade befinden, sodass Ihr Programm seinen fröhlichen Weg fortsetzen kann.
Der Heap ist der Bereich, aus dem dynamische Speicherzuordnungen für den Speicher erstellt werden (explizite "neue" oder "zugewiesene" Aufrufe). Es handelt sich um eine spezielle Datenstruktur, die Speicherblöcke unterschiedlicher Größe und deren Zuordnungsstatus verfolgen kann.
In "klassischen" Systemen wurde RAM so angelegt, dass der Stapelzeiger am unteren Rand des Speichers begann, der Heap-Zeiger am oberen Rand begann und sie aufeinander zuwuchsen. Wenn sie sich überschneiden, haben Sie keinen RAM mehr. Dies funktioniert jedoch nicht mit modernen Multithread-Betriebssystemen. Jeder Thread muss einen eigenen Stapel haben, und diese können dynamisch erstellt werden.
quelle
Von WikiAnwser.
Stapel
Wenn eine Funktion oder eine Methode eine andere Funktion aufruft, die wiederum eine andere Funktion usw. aufruft, bleibt die Ausführung all dieser Funktionen ausgesetzt, bis die allerletzte Funktion ihren Wert zurückgibt.
Diese Kette angehaltener Funktionsaufrufe ist der Stapel, da Elemente im Stapel (Funktionsaufrufe) voneinander abhängen.
Der Stapel ist wichtig bei der Ausnahmebehandlung und Thread-Ausführung.
Haufen
Der Heap ist einfach der Speicher, der von Programmen zum Speichern von Variablen verwendet wird. Elemente des Heaps (Variablen) haben keine Abhängigkeiten voneinander und können jederzeit nach dem Zufallsprinzip aufgerufen werden.
quelle
Stapel
Haufen
quelle
OK, einfach und in kurzen Worten, sie bedeuten bestellt und nicht bestellt ...!
Stapel : Bei Stapelgegenständen überlagern sich die Dinge, was bedeutet, dass die Verarbeitung schneller und effizienter ist! ...
Es gibt also immer einen Index, der auf das jeweilige Element verweist. Außerdem wird die Verarbeitung schneller, es besteht auch eine Beziehung zwischen den Elementen! ...
Haufen : Keine Reihenfolge, die Verarbeitung wird langsamer und die Werte werden ohne bestimmte Reihenfolge oder Index durcheinander gebracht. Es gibt zufällige und es gibt keine Beziehung zwischen ihnen. Daher können Ausführungs- und Verwendungszeit variieren.
Ich erstelle auch das Bild unten, um zu zeigen, wie sie aussehen können:
quelle
Zusamenfassend
Ein Stapel 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 Stapel
Der Stack ist eine "LIFO" -Datenstruktur (last in, first out), die von der CPU sehr genau verwaltet und optimiert wird. Jedes Mal, wenn eine Funktion eine neue Variable deklariert, wird sie auf den Stapel "geschoben". Jedes Mal, wenn eine Funktion beendet wird, werden alle von dieser Funktion auf den Stapel geschobenen Variablen freigegeben (dh sie werden gelöscht). Sobald eine Stapelvariable freigegeben ist, wird dieser Speicherbereich für andere Stapelvariablen verfügbar.
Der Vorteil der Verwendung des Stapels zum Speichern von Variablen besteht darin, dass der Speicher für Sie verwaltet wird. Sie müssen den Speicher nicht manuell zuweisen oder freigeben, wenn Sie ihn nicht mehr benötigen. Da die CPU den Stapelspeicher so effizient organisiert, ist das Lesen und Schreiben von Stapelvariablen sehr schnell.
Mehr finden Sie hier .
Der Haufen
Der Heap ist ein Bereich des Arbeitsspeichers Ihres Computers, der nicht automatisch für Sie verwaltet wird und von der CPU nicht so streng verwaltet wird. Es ist ein frei schwebender Speicherbereich (und größer). Um Speicher auf dem Heap zuzuweisen, müssen Sie malloc () oder calloc () verwenden, die integrierte C-Funktionen sind. Sobald Sie Speicher auf dem Heap zugewiesen haben, sind Sie dafür verantwortlich, free () zu verwenden, um die Zuweisung dieses Speichers aufzuheben, sobald Sie ihn nicht mehr benötigen.
Wenn Sie dies nicht tun, weist Ihr Programm einen sogenannten Speicherverlust auf. Das heißt, der Speicher auf dem Heap wird weiterhin reserviert (und steht anderen Prozessen nicht zur Verfügung). Wie wir im Abschnitt zum Debuggen sehen werden, gibt es ein Tool namens Valgrind , mit dem Sie Speicherlecks erkennen können.
Im Gegensatz zum Stapel unterliegt der Heap keinen Größenbeschränkungen für die variable Größe (abgesehen von den offensichtlichen physischen Einschränkungen Ihres Computers). Der Heap-Speicher ist etwas langsamer zum Lesen und Schreiben, da Zeiger verwendet werden müssen, um auf den Speicher des Heaps zuzugreifen. Wir werden in Kürze über Hinweise sprechen.
Im Gegensatz zum Stapel können Variablen, die auf dem Heap erstellt wurden, von jeder Funktion an einer beliebigen Stelle in Ihrem Programm aufgerufen werden. Heap-Variablen sind im Wesentlichen global.
Mehr finden Sie hier .
Auf dem Stapel zugewiesene Variablen werden direkt im Speicher gespeichert, und der Zugriff auf diesen Speicher ist sehr schnell, und seine Zuordnung wird beim Kompilieren des Programms behandelt. Wenn eine Funktion oder eine Methode eine andere Funktion aufruft, die wiederum eine andere Funktion usw. aufruft, bleibt die Ausführung all dieser Funktionen ausgesetzt, bis die allerletzte Funktion ihren Wert zurückgibt. Der Stapel wird immer in einer LIFO-Reihenfolge reserviert. Der zuletzt reservierte Block ist immer der nächste freizugebende Block. Dies macht es wirklich einfach, den Stapel im Auge zu behalten. Das Lösen eines Blocks vom Stapel ist nichts anderes als das Anpassen eines Zeigers.
Auf dem Heap zugewiesenen Variablen wird zur Laufzeit der Speicher zugewiesen, und der Zugriff auf diesen Speicher ist etwas langsamer, die Größe des Heapspeichers ist jedoch nur durch die Größe des virtuellen Speichers begrenzt. Elemente des Heaps haben keine Abhängigkeiten voneinander und können jederzeit nach dem Zufallsprinzip aufgerufen werden. Sie können einen Block jederzeit zuweisen und jederzeit freigeben. Dies macht es viel komplexer zu verfolgen, welche Teile des Heaps zu einem bestimmten Zeitpunkt zugewiesen oder frei sind.
Sie können den Stapel verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierungszeit zuweisen müssen, und er 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 viele Daten zuweisen müssen.
In einer Situation mit mehreren Threads hat jeder Thread seinen eigenen, völlig unabhängigen Stapel, aber sie teilen sich den Heap. Der Stack ist threadspezifisch und der Heap ist anwendungsspezifisch. Der Stapel ist wichtig bei der Ausnahmebehandlung und Thread-Ausführung.
Jeder Thread erhält einen Stapel, während es normalerweise nur einen Heap für die Anwendung gibt (obwohl es nicht ungewöhnlich ist, mehrere Heaps für verschiedene Zuordnungstypen zu haben).
Wenn die Anwendung zur Laufzeit mehr Heap benötigt, kann sie Speicher aus dem freien Speicher zuweisen, und wenn der Stapel Speicher benötigt, kann sie Speicher aus dem freien Speicher zuweisen, der der Anwendung zugewiesen ist.
Noch mehr Details werden hier und hier gegeben .
Kommen Sie nun zu den Antworten Ihrer Frage .
Das Betriebssystem weist den Stapel jedem Thread auf Systemebene zu, wenn der Thread erstellt wird. In der Regel wird das Betriebssystem von der Sprachlaufzeit aufgerufen, um den Heap für die Anwendung zuzuweisen.
Mehr finden Sie hier .
Bereits oben angegeben.
Mehr finden Sie hier .
Die Größe des Stapels wird vom Betriebssystem festgelegt, wenn ein Thread erstellt wird. Die Größe des Heapspeichers wird beim Start der Anwendung festgelegt, kann jedoch bei Bedarf an Speicherplatz zunehmen (der Allokator fordert mehr Speicher vom Betriebssystem an).
Die Stapelzuweisung ist viel schneller, da nur der Stapelzeiger bewegt wird. Wenn Sie Speicherpools verwenden, können Sie eine vergleichbare Leistung bei der Heap-Zuweisung erzielen. Dies ist jedoch mit einer geringfügig zusätzlichen Komplexität und eigenen Kopfschmerzen verbunden.
Außerdem ist Stack vs. Heap nicht nur eine Leistungsüberlegung. Außerdem erfahren Sie viel über die erwartete Lebensdauer von Objekten.
Details finden Sie hier .
quelle
In den 1980er Jahren verbreitete sich UNIX wie ein Hase, und große Unternehmen rollten ihre eigenen. Exxon hatte einen, ebenso wie Dutzende von Markennamen, die der Geschichte verloren gingen. Wie der Speicher angelegt wurde, lag im Ermessen der vielen Implementierer.
Ein typisches C-Programm wurde flach im Speicher angelegt, mit der Möglichkeit, durch Ändern des brk () -Werts zu erhöhen. Typischerweise lag der HEAP knapp unter diesem brk-Wert und eine Erhöhung von brk erhöhte die Menge des verfügbaren Heaps.
Der einzelne STACK war typischerweise ein Bereich unterhalb von HEAP, der ein Speicherbereich war, der bis zum oberen Rand des nächsten festen Speicherblocks nichts Wertvolles enthielt. Dieser nächste Block war oft CODE, der durch Stapeldaten in einem der berühmten Hacks seiner Zeit überschrieben werden konnte.
Ein typischer Speicherblock war BSS (ein Block mit Nullwerten), der im Angebot eines Herstellers versehentlich nicht auf Null gesetzt wurde. Ein anderes war DATA, das initialisierte Werte enthielt, einschließlich Zeichenfolgen und Zahlen. Ein dritter war CODE, der CRT (C-Laufzeit), main, Funktionen und Bibliotheken enthielt.
Das Aufkommen des virtuellen Speichers unter UNIX ändert viele der Einschränkungen. Es gibt keinen objektiven Grund, warum diese Blöcke zusammenhängend oder in der Größe festgelegt oder jetzt auf eine bestimmte Weise bestellt werden müssen. Natürlich gab es vor UNIX Multics, die nicht unter diesen Einschränkungen litten. Hier ist ein Schema, das eines der Speicherlayouts dieser Ära zeigt.
quelle
Stapel , Heap und Daten jedes Prozesses im virtuellen Speicher:
quelle
Ein paar Cent: Ich denke, es wird gut sein, Speicher grafisch und einfacher zu zeichnen:
Pfeile - zeigen an, wo der Stapel und der Heap wachsen, die Größe des Prozessstapels begrenzt ist, definiert im Betriebssystem, die Größe des Thread-Stacks durch Parameter in der Thread-Erstellungs-API normalerweise. Heap, der normalerweise die maximale Größe des virtuellen Speichers pro Prozess begrenzt, z. B. für 32 Bit 2-4 GB.
So einfach: Der Prozessheap ist allgemein für den Prozess und alle darin enthaltenen Threads und wird für die Speicherzuweisung verwendet, wie dies bei malloc () üblich ist .
Der Stapel ist ein schneller Speicher zum Speichern von Funktionsrückgabezeigern und -variablen im allgemeinen Fall, die als Parameter im Funktionsaufruf und als lokale Funktionsvariablen verarbeitet werden.
quelle
Da einige Antworten nicht ausgewählt wurden, werde ich meine Milbe beisteuern.
Überraschenderweise hat niemand erwähnt, dass mehrere (dh nicht mit der Anzahl der laufenden Threads auf Betriebssystemebene zusammenhängende) Aufrufstapel nicht nur in exotischen Sprachen (PostScript) oder Plattformen (Intel Itanium), sondern auch in Fasern , grünen Threads, zu finden sind und einige Implementierungen von Coroutinen .
Fasern, grüne Fäden und Coroutinen sind in vielerlei Hinsicht ähnlich, was zu viel Verwirrung führt. Der Unterschied zwischen Fasern und grünen Fäden besteht darin, dass die ersteren kooperatives Multitasking verwenden, während die letzteren entweder kooperatives oder präventives (oder sogar beides) aufweisen können. Zur Unterscheidung zwischen Fasern und Coroutinen siehe hier .
In jedem Fall besteht der Zweck von Fasern, grünen Threads und Coroutinen darin, dass mehrere Funktionen gleichzeitig, jedoch nicht parallel (siehe diese SO-Frage zur Unterscheidung) innerhalb eines einzelnen Threads auf Betriebssystemebene ausgeführt werden und die Steuerung voneinander hin und her übertragen wird auf organisierte Weise.
Wenn Sie Fasern, grüne Fäden oder Coroutinen verwenden, haben Sie normalerweise einen separaten Stapel pro Funktion. (Technisch gesehen ist nicht nur ein Stapel, sondern ein ganzer Ausführungskontext pro Funktion. Am wichtigsten ist, dass sich die CPU registriert.) Für jeden Thread gibt es so viele Stapel, wie gleichzeitig Funktionen ausgeführt werden, und der Thread wechselt zwischen der Ausführung jeder Funktion gemäß der Logik Ihres Programms. Wenn eine Funktion zu Ende geht, wird ihr Stapel zerstört. Also, die Anzahl und die Lebensdauer der Stacks sind dynamisch und werden nicht durch die Anzahl der OS-Level - Threads bestimmt!
Beachten Sie, dass ich sagte " normalerweise einen separaten Stapel pro Funktion haben". Es gibt sowohl stapelbar als auch stapellos Implementierungen von Couroutinen. Am bemerkenswertesten stackful C ++ Implementierungen sind Boost.Coroutine und Microsoft PPL s‘
async/await
. (Die wiederaufnehmbaren Funktionen von C ++ (auch bekannt als "async
undawait
"), die in C ++ 17 vorgeschlagen wurden, verwenden wahrscheinlich stapellose Coroutinen.)Der Vorschlag für Fasern zur C ++ - Standardbibliothek ist in Vorbereitung. Es gibt auch einige Bibliotheken von Drittanbietern . Grüne Fäden sind in Sprachen wie Python und Ruby äußerst beliebt.
quelle
Ich habe etwas zu teilen, obwohl die wichtigsten Punkte bereits behandelt werden.
Stapel
Haufen
Interessanter Hinweis:
quelle
Beeindruckend! So viele Antworten und ich glaube nicht, dass einer von ihnen es richtig gemacht hat ...
1) Wo und was sind sie (physisch im Speicher eines realen Computers)?
Der Stapel ist ein Speicher, der als höchste Speicheradresse beginnt, die Ihrem Programmabbild zugewiesen ist, und von dort aus an Wert verliert. Es ist für aufgerufene Funktionsparameter und für alle in Funktionen verwendeten temporären Variablen reserviert.
Es gibt zwei Haufen: öffentliche und private.
Der private Heap beginnt an einer 16-Byte-Grenze (für 64-Bit-Programme) oder einer 8-Byte-Grenze (für 32-Bit-Programme) nach dem letzten Codebyte in Ihrem Programm und nimmt von dort aus an Wert zu. Es wird auch als Standardheap bezeichnet.
Wenn der private Heap zu groß wird, überlappt er den Stapelbereich, ebenso wie der Stapel den Heap, wenn er zu groß wird. Da der Stapel an einer höheren Adresse beginnt und sich zu einer niedrigeren Adresse hinunterarbeitet, können Sie den Stapel bei ordnungsgemäßem Hacking so groß machen, dass er den privaten Heap-Bereich überläuft und den Codebereich überlappt. Der Trick besteht dann darin, den Codebereich so weit zu überlappen, dass Sie ihn in den Code einbinden können. Es ist etwas schwierig und Sie riskieren einen Programmabsturz, aber es ist einfach und sehr effektiv.
Der öffentliche Heap befindet sich in seinem eigenen Speicherbereich außerhalb Ihres Programmabbildbereichs. Dieser Speicher wird auf die Festplatte übertragen, wenn die Speicherressourcen knapp werden.
2) Inwieweit werden sie vom Betriebssystem oder der Sprachlaufzeit gesteuert?
Der Stack wird vom Programmierer gesteuert, der private Heap wird vom Betriebssystem verwaltet und der öffentliche Heap wird von niemandem gesteuert, da es sich um einen Betriebssystemdienst handelt. Sie stellen Anforderungen und entweder werden sie gewährt oder abgelehnt.
2b) Was ist ihr Umfang?
Sie sind alle global für das Programm, aber ihre Inhalte können privat, öffentlich oder global sein.
2c) Was bestimmt die Größe von jedem von ihnen?
Die Größe des Stacks und des privaten Heaps wird durch die Laufzeitoptionen Ihres Compilers bestimmt. Der öffentliche Heap wird zur Laufzeit mithilfe eines Größenparameters initialisiert.
2d) Was macht einen schneller?
Sie sind nicht so konzipiert, dass sie schnell sind, sondern so, dass sie nützlich sind. Wie der Programmierer sie verwendet, bestimmt, ob sie "schnell" oder "langsam" sind.
REF:
https://norasandler.com/2019/02/18/Write-a-Compiler-10.html
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate
quelle
Viele Antworten sind als Konzepte korrekt, aber wir müssen beachten, dass die Hardware (dh der Mikroprozessor) einen Stapel benötigt, um Unterprogramme aufrufen zu können (CALL in Assemblersprache ..). (OOP Jungs werden es Methoden nennen )
Auf dem Stack speichern Sie Absenderadressen und Aufruf → Push / Ret → Pop wird direkt in der Hardware verwaltet.
Sie können den Stapel verwenden, um Parameter zu übergeben. Auch wenn er langsamer ist als die Verwendung von Registern (würde ein Mikroprozessor-Guru sagen oder ein gutes BIOS-Buch aus den 1980er Jahren ...).
Die Stapelnutzung ist schneller als:
quelle
malloc
ein Kernel-Aufruf ist?Quelle: Academind
quelle
Vielen Dank für eine wirklich gute Diskussion, aber als echter Neuling frage ich mich, wo Anweisungen aufbewahrt werden? Am Anfang entschieden sich Wissenschaftler zwischen zwei Architekturen (von NEUMANN, wo alles als DATA betrachtet wird, und HARVARD, wo ein Speicherbereich für Anweisungen und ein anderer für Daten reserviert war). Letztendlich haben wir uns für das von Neumann-Design entschieden und jetzt gilt alles als "gleich". Dies machte es mir schwer, als ich Assembly https://www.cs.virginia.edu/~evans/cs216/guides/x86.html lernte, weil sie über Register und Stapelzeiger sprechen.
Alles oben spricht über DATEN. Ich vermute, dass ein Befehl, da er eine definierte Sache mit einem bestimmten Speicherbedarf ist, auf dem Stapel abgelegt wird und sich daher alle in der Assembly diskutierten 'diese' Register auf dem Stapel befinden. Natürlich kam dann die objektorientierte Programmierung mit Anweisungen und Daten, die in eine dynamische Struktur kamen, sodass jetzt auch Anweisungen auf dem Heap bleiben würden?
quelle