Geschrieben von: Robert Mertens | Letztes Update: 

Stack und Heap

Im allgemeinen bezeichnen Stack (“Stapel”) und Heap (“Haufen”) Datenstrukturen mit ihren ganz speziellen Eigenschaften. Dieser Beitrag bezieht sich aber auf die konkrete Verwendung für die Bereitstellung von Speicher für ein ausführbares Programm. Die folgenden Grundsätze gelten für die meisten aktuellen Programmiersprachen.

Zusammenfassung

  • Stack und Heap sind Teile des Arbeitsspeichers.
  • Der Stack ist eine LIFO-Datenstruktur und sehr effizient.
  • Der Heap kann beliebig groß werden, ist aber langsamer zu verwalten.
  • Objekte auf dem Stack werden automatisch freigegeben, während der Heap manuell freigegeben werden muss.
  • In einigen Programmiersprachen kann man die Verwendung von Stack und Heap beeinflussen.
  • Ein Speicherleck entsteht, wenn ein mit “new” angelegtes Objekt auf dem Heap nicht gelöscht wird.
  • Der Heap wird für dynamische Speicheranforderungen genutzt, während der Stack lokale Variablen und Funktionsparameter speichert.
  • Ein Heap ist eine Datenstruktur zur Sortierung und Prioritätswarteschlangen.
  • Ein Stack ist eine dynamische Datenstruktur mit LIFO-Prinzip.Auf dem Stack befinden sich neue Elemente über den bereits vorhandenen Elementen.

Stack und Heap sind Teile des Arbeitsspeichers

Im Kontext der Speicherverwaltung auf Programmebene handelt es sich also sowohl beim Stack als auch beim Heap um Teile des Arbeitsspeichers, die vom Betriebssystem dem ausführenden Programm zur Verfügung gestellt werden. Um genauer zu sein handelt es sich um einen Bereich des virtuellen Speichers. Dadurch ist für den Prozess der tatsächliche physikalische Speicherort eines Objekts nicht bekannt. Die Daten können im Hintergrund irgendwo im Arbeitsspeicher liegen oder sogar auf die Festplatte ausgelagert sein.
Der Stack

Der Name deutet an, dass die Daten hier “aufeinander” liegen. Damit ist gemeint, dass neue Daten immer nur oben drauf gelegt werden können. Wenn die Daten wieder freigegeben werden, werden Sie von oben nach unten wieder entfernt. Dieses Prinzip nennt sich auch LIFO (“Last in, First out”). Der Stack kann, bedingt durch seine Struktur, sehr effizient verwaltet werden, weshalb Stack-Operationen sehr schnell sind.

Jeder Thread eines Programmes erhält für den Stack einen eigenen Speicherbereich mit fixer Größe zugewiesen. Darauf werden Informationen zum Programmablauf (z.B. Funktionsparameter) und lokale Variablen gespeichert. Beim Anlegen neuer lokaler Variablen wächst der Stack an und beim Verlassen des Sichtbarkeitsbereichs (“Scope”) schrumpft er wieder und der Speicher wird automatisch aufgeräumt. Typische Stackgrößen variieren zwischen 64 KB und 8 MB. Einstellen (auch wenn das im Normalfall nicht nötig ist) kann man die Stackgröße meist über die Entwicklungsumgebung, aber auch das Betriebssystem kann natürlich Einfluss nehmen.

Objekt auf dem Stack anlegen

{
// Create a new object on the stack
CExampleClassBase myObject;

// Use ‘.’-operator for function calls through variable
myObject.Print();

} // end of scope, stack variables will be deleted, myObject’s destructor will be called

Dieses Beispiel zeigt, wie in C++ auf dem Stack ein Objekt angelegt wird. Die geschwungenen Klammern definieren einen Sichtbarkeitsbereich, innerhalb dessen die Stack-Variable gültig bleibt. Beim Verlassen des Sichtbarkeitsbereichs wird das Objekt zerstört (der Destruktor wird aufgerufen) und der Stack wieder geschrumpft (d.h. der Speicher wird wieder freigegeben).

Nochmal zusammengefasst die Eigenschaften des Stacks:

  • Begrenzte Größe
  • LIFO Datenstruktur (die zuletzt angelegten Daten werden als erstes wieder freigegeben, deshalb auch “Stapel”)
  • Wächst und schrumpft mit dem Programmverlauf
  • Wird verwendet für lokale Variablen und Funktionsparameter
  • Kein explizites Freigeben des Speichers nötig
  • Das Ablegen und Entfernen von Elementen ist sehr effizient

Der Heap

Der Heap ist nicht so strukturiert wie der Stack. Du kannst ihn dir tatsächlich als Haufen vorstellen, auf dem jede Menge Platz ist. Während der Stack nämlich von der Größe her stark begrenzt ist, kann der Heap anwachsen bis die Speichergrenze auf Prozessebene erreicht ist. Dafür ist der Heap aber intern nicht so einfach zu verwalten, was ihn langsamer als den Stack macht. Auf dem Heap angelegter Speicher muss auch explizit wieder freigegeben werden (durch den Programmierer oder z.B. den Garbage Collector, je nach Programmiersprache).

Für den Zugriff auf den Heap werden Zeiger verwendet auch wenn das nicht immer direkt ersichtlich ist. Java und C# verwenden zum Beispiel Referenzen für Objekte die auf dem Heap liegen. Im Hintergrund muss aber natürlich trotzdem mit Zeigern gearbeitet werden, welche die Speicheradresse des Objektes beinhalten.

Da auf dem Heap angelegte Objekte nicht auf den lokalen Sichtbarkeitsbereich beschränkt sind, kann global darauf zugegriffen werden (sofern ein Zeiger oder eine Referenz vorhanden ist).

Objekt auf dem Heap anlegen

// Create a new object on the heap and retrieve pointer to it
CExampleClassBase* myObject = new CExampleClassDerived();

// Use ‘->’-operator for function calls through pointer
myObject->Print();

delete myObject;
myObject = NULL; // NULL maybe replaced by ‘nullptr’ since C++11

Hier wird ein Objekt in C++ mit Hilfe des new-Operators auf dem Heap angelegt. Nach Verwendung muss das Objekt ausdrücklich mit delete wieder freigegeben werden. Das Zurücksetzen des Pointers beugt einem Zugriff auf bereits freigegebenen Speicher vor, denn delete gibt zwar den Speicher frei, setzt aber den Zeiger nicht auf NULL zurück. Wenn das delete erst zu einem späteren Zeitpunkt ausgeführt wird (z.B. beim Beenden des Programms), kann der Zeiger beliebig an andere Objekte verteilt werden, die dann ebenfalls Zugriff auf das Objekt erhalten.

Die Eigenschaften des Heaps lassen sich also wie folgt zusammenfassen:

  • Der Heap kann innerhalb der Prozessgrenze beliebig groß werden
  • Anlegen und freigeben von Objekten ist vergleichsweise langsam
  • Auf dem Heap angelegte Objekte können global verfügbar gemacht werden
  • In Programmiersprachen ohne Garbage Collector muss der Speicher manuell freigegeben werden, wenn er nicht mehr benötigt wird

Anwendung

In manchen Programmiersprachen (z.B. Java) hat man keinen direkten Einfluss auf die Verwendung von Stack oder Heap. In C++ besteht diese Möglichkeit aber schon. Es können zum Beispiel Objekte auf dem Stack angelegt werden. Das macht Sinn, wenn das Objekt nur kurzzeitig, also innerhalb des Sichtbarkeitsbereichs, gebraucht wird. Meistens gilt aber in C++ auch, dass man Objekte mit dem “new”-Operator auf dem Heap anlegt und für den Rest den Stack verwendet. Wenn du vergisst, ein mit “new” angelegtes Objekt wieder zu löschen, entsteht ein Speicherleck (“Memory Leak”).

FAQ: Häufige Fragen zu Stack und Heap

Warum Stack und Heap?

Eine Speicheranforderung vom Heap kann auch als dynamische Speicheranforderung bezeichnet werden. Programme nutzen ihn, um über Pufferspeicher zu verfügen, der nicht von ihrem Programmcode und festen reservierten Feldern und dem Stack belegt ist.

Was ist ein Heap in der Informatik und im Computerwesen?

Ein Heap ist eine Datenstruktur, die in der Informatik verwendet wird, um Daten zu sortieren und Prioritätswarteschlangen zu erstellen.

Wie wird der Heap größer?

Ein Heap kann sowohl als Baum als auch als Array dargestellt werden. Ein binärer Heap zum Beispiel ist ein binärer Baum. Jeder Knoten kann maximal zwei Kinder haben. Wie bei Baumstrukturen wächst der Heap von der Wurzel abwärts und von links nach rechts.

Was ist ein Stack?

Ein Stack ist ein Begriff aus der Informatik, der sich auf eine dynamische Datenstruktur bezieht. Er kann von den meisten Mikroprozessoren, die Maschinenbefehle verwenden, direkt unterstützt werden.

Was befindet sich auf dem Stack?

Stack Ein Speicherbereich, in dem neue Elemente über den bereits vorhandenen Elementen gespeichert werden. Freigaben müssen in umgekehrter Reihenfolge erfolgen (d. h. oben zuerst). (LIFO = Last in First Out). Statische Variablen Diese statischen Variablen werden zu Beginn des Programms erstellt und am Ende des Programms wieder gelöscht.