Was und wo sind der Stack und der Heap?

In Büchern über Programmiersprachen wird erklärt, dass Werttypen auf dem Stapel und Referenztypen auf dem Haufen erstellt werden, ohne zu erklären, was diese beiden Dinge sind. Ich habe keine klare Erklärung dazu 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 kontrolliert?
  • Was ist ihr Umfang?
  • Wodurch wird die Größe eines jeden von ihnen bestimmt?
  • Wodurch wird eine schneller?

Stapel:

  • Wird genau wie der Heap im RAM des Computers gespeichert.
  • Variablen, die auf dem Stack erstellt werden, gehen aus dem Anwendungsbereich heraus und werden automatisch wieder freigegeben.
  • Im Vergleich zu Variablen auf dem Heap viel schneller zuzuordnen.
  • Implementiert mit einer tatsächlichen Stack-Datenstruktur.
  • Speichert lokale Daten, Rücksprungadressen, wird für die Parameterübergabe verwendet.
  • Es kann zu einem Stack-Überlauf kommen, wenn zu viel vom Stack verwendet wird (meist durch unendliche oder zu tiefe Rekursion, sehr große Zuweisungen).
  • Auf dem Stack erzeugte Daten können ohne Zeiger verwendet werden.
  • Sie sollten den Stack verwenden, wenn Sie genau wissen, wie viele Daten Sie vor der Kompilierung zuweisen müssen und er nicht zu groß ist.
  • Normalerweise ist die maximale Größe bereits beim Start des Programms festgelegt.

Heap:

  • Wird genau wie der Stack im RAM des Computers gespeichert.
  • In C++ müssen Variablen auf dem Heap manuell zerstört werden und fallen nie aus dem Anwendungsbereich heraus. Die Daten werden mit delete, delete[], oder free freigegeben.
  • Langsamer bei der Zuweisung im Vergleich zu Variablen auf dem Stack.
  • Wird bei Bedarf verwendet, um einen Datenblock zur Verwendung durch das Programm zuzuweisen.
  • Kann zu Fragmentierung führen, wenn es viele Zuweisungen und Freigaben gibt.
  • In C++ oder C werden Daten, die auf dem Heap erzeugt werden, mit Zeigern gekennzeichnet und mit new bzw. malloc zugewiesen.
  • Es kann zu Zuweisungsfehlern kommen, wenn ein zu großer Puffer zur Zuweisung angefordert wird.
  • Sie sollten den Heap verwenden, wenn Sie nicht genau wissen, wie viele Daten Sie zur Laufzeit benötigen oder wenn Sie viele Daten zuweisen müssen.
  • Verantwortlich für Speicherlecks.

Beispiel:


int foo()
{
  char *pBuffer; //
Kommentare (18)

Der Stack Wenn Sie eine Funktion aufrufen, werden die Argumente für diese Funktion und einige andere Daten auf dem Stack abgelegt. Einige Informationen (z. B. wohin bei der Rückkehr) werden ebenfalls dort gespeichert. Wenn Sie eine Variable innerhalb Ihrer Funktion deklarieren, wird diese Variable ebenfalls auf dem Stack abgelegt.

Die Deallokation des Stacks ist recht einfach, da sie immer in der umgekehrten Reihenfolge wie die Allokation erfolgt. Beim Eintritt in eine Funktion wird Stapelmaterial hinzugefügt, beim Verlassen der Funktion werden die entsprechenden Daten entfernt. Das bedeutet, dass man in der Regel innerhalb eines kleinen Bereichs des Stacks bleibt, es sei denn, man ruft viele Funktionen auf, die wiederum viele andere Funktionen aufrufen (oder eine rekursive Lösung erstellen).

Der Heap Der Heap ist ein allgemeiner Name für den Ort, an dem Sie die Daten ablegen, die Sie im laufenden Betrieb erstellen. Wenn Sie nicht wissen, wie viele Raumschiffe Ihr Programm erstellen wird, werden Sie wahrscheinlich den Operator new (oder malloc oder einen vergleichbaren Operator) verwenden, um jedes Raumschiff zu erstellen. Diese Zuweisung wird eine Zeit lang bestehen bleiben, so dass es wahrscheinlich ist, dass wir die Dinge in einer anderen Reihenfolge freigeben, als wir sie erstellt haben.

Dadurch wird der Heap wesentlich komplexer, da sich ungenutzte Speicherbereiche mit freien Speicherbereichen abwechseln - der Speicher wird fragmentiert. Die Suche nach freiem Speicher in der benötigten Größe ist ein schwieriges Problem. Aus diesem Grund sollte der Heap vermieden werden (auch wenn er noch oft verwendet wird).

Implementierung Die Implementierung sowohl des Stacks als auch des Heaps obliegt in der Regel der Laufzeitumgebung/dem Betriebssystem. Spiele und andere Anwendungen, bei denen es auf die Leistung ankommt, entwickeln oft ihre eigenen Speicherlösungen, die einen großen Teil des Speichers aus dem Heap nehmen und ihn dann intern verteilen, um sich nicht auf das Betriebssystem verlassen zu müssen.

Dies ist nur dann sinnvoll, wenn die Speichernutzung von der Norm abweicht - z. B. bei Spielen, bei denen man einen Level in einem einzigen großen Vorgang lädt und den gesamten Speicher in einem weiteren großen Vorgang wieder wegschmeißen kann.

Physikalischer Ort im Speicher Dies ist weniger relevant, als Sie denken, denn es gibt eine Technologie namens Virtueller Speicher, die Ihr Programm glauben lässt, dass Sie Zugriff auf eine bestimmte Adresse haben, während sich die physischen Daten irgendwo anders befinden (sogar auf der Festplatte!). Die Adressen, die Sie für den Stack erhalten, sind in aufsteigender Reihenfolge, je tiefer Ihr Aufrufbaum wird. Die Adressen für den Heap sind nicht vorhersehbar (d.h. implimentationsspezifisch) und offen gesagt nicht wichtig.

Kommentare (5)

Der Stack ist ein Teil des Speichers, der mit mehreren wichtigen Assembler-Befehlen manipuliert werden kann, z. B. "pop" (einen Wert vom Stack entfernen und zurückgeben) und "push" (einen Wert auf den Stack schieben), aber auch "call" (ein Unterprogramm aufrufen - damit wird die Adresse für die Rückkehr auf den Stack geschoben) und "return" (Rückkehr aus einem Unterprogramm - damit wird die Adresse vom Stack entfernt und dorthin gesprungen). Der Stack ist der Speicherbereich unterhalb des Stack-Pointer-Registers, der je nach Bedarf eingestellt werden kann. Der Stack wird auch für die Übergabe von Argumenten an Unterprogramme und für die Aufbewahrung der Werte in Registern vor dem Aufruf von Unterprogrammen verwendet.

Der Heap ist ein Teil des Speichers, der einer Anwendung vom Betriebssystem zur Verfügung gestellt wird, normalerweise durch einen Syscall wie malloc. Bei modernen Betriebssystemen ist dieser Speicher ein Satz von Seiten, auf die nur der aufrufende Prozess Zugriff hat.

Die Größe des Stacks wird zur Laufzeit bestimmt und wächst im Allgemeinen nicht mehr an, nachdem das Programm gestartet wurde. In einem C-Programm muss der Stack groß genug sein, um jede in jeder Funktion deklarierte Variable aufzunehmen. Der Heap wächst bei Bedarf dynamisch an, aber der Aufruf erfolgt letztlich durch das Betriebssystem (es vergrößert den Heap oft um mehr als den von malloc angeforderten Wert, so dass zumindest einige künftige mallocs nicht zum Kernel zurückkehren müssen, um mehr Speicher zu erhalten. Dieses Verhalten ist oft anpassbar)

Da Sie den Stack vor dem Start des Programms allokiert haben, müssen Sie niemals malloc ausführen, bevor Sie den Stack verwenden können, was einen kleinen Vorteil darstellt. In der Praxis ist es sehr schwer vorherzusagen, was in modernen Betriebssystemen mit virtuellem Speicher-Subsystem schnell und was langsam sein wird, denn wie die Seiten implementiert sind und wo sie gespeichert werden, ist ein Detail der Implementierung.

Kommentare (1)