Stack und Heap in .NET/C#

Stack und Heap in .NET/C#

Selbst erfahrene C#-Entwickler kann man mit Fragen nach Stack und Heap ins Schwimmen bringen. Deshalb hier ein kurzer Refresher zu dem Thema.

Stack – der kleine, schnelle Speicher

Der Stack ist ein sehr schneller, linearer Speicherbereich, der vom Betriebssystem für jeden Thread bereitgestellt wird. Er wird für die Verwaltung von lokalen Variablen und Funktionsaufrufen genutzt. Hier speichert .NET/C# Werte von Werttypen (wie int, double, bool, structs), die als lokale Variablen in Methoden deklariert sind. Außerdem werden hier Funktionsaufrufe (Call Stack, Rücksprungadressen, lokale Variablen) abgelegt.
Speicher in diesem Bereich wird automatisch freigegeben, sobald eine Methode verlassen wird, aber das Betriebssystem begrenzt den Speicher standardmäßig auf 1 MB pro Thread (ASP.NET Core: 256KB), wenn der Entwickler nichts anderes angibt. Stack-Variablen existieren also nur so lange wie die entsprechende Methode aktiv ist.

Heap – der Objektspeicher

Der Heap ist ein Speicherbereich für die dynamische Verwaltung von Daten. Hier werden Referenztypen abgelegt, also Objekte und Arrays – und alle Daten, die über die Lebensdauer einer Methode hinaus existieren sollen. Theoretisch ist der Heap nur durch die Größe des RAM der Maschine begrenzt.
Die Speicherverwaltung erfolgt hier durch den Garbage Collector von .NET: Nicht mehr benötigte Objekte werden automatisch freigegeben, die Lebensdauer von Daten ist nicht an die Lebensdauer von Methoden gebunden.

Zusammenspiel von Stack und Heap

Nehmen wir an, wir erzeugen eine Objekt-Instanz:

Chicken huhn = new Chicken() { Name = "Hilde", Weight = 1250 };

Der Konstruktoraufruf new Chicken() reserviert im Heap einen Bereich für die Daten des Huhns. Die Variable huhn jedoch ist eine lokale Variable und liegt daher auf dem Stack. Aber: huhn enthält nicht die Daten des Objekts, sondern nur die Adresse (Referenz), wo die Instanz im Heap gespeichert ist. Das Gewicht des Tiers, obwohl ein Wertetyp (int, double,...), liegt ebenfalls im Heap, weil es Teil des Objekts Chicken ist! Noch einen Schritt weiter geht die Name-Property unsers Huhns, denn die Chicken-Instanz enthält in ihrem Heap-Bereich einen Zeiger(!) auf das eigentlich String-Objekt, das ebenfalls im Heap zu finden ist.
Kurz gesagt: Außer der Variable, die auf die Objekt-Instanz verweist, liegen bei Referenztypen alle Daten im Heap!
Und weil der Heap von der Garbage Collection verwaltet wird, ist in C# im Zweifel explizites Disposing bei komplexeren Objektstrukturen immanent wichtig.