Der Typ eines Datenelements bestimmt zwei Dinge: die benötigte Speichergröße (Anzahl der Bits) zur Ablage sowie alle im Typ enthaltenen Datenmember. Zusätzlich legt der Typ fest, ob ein Objekt im Speicher auf dem Stack (Stapelspeicher) oder dem Heap (Haldenspeicher) abgelegt wird.
Typen unterteilen sich in zwei Hauptgruppen: Werttypen (Value Types) und Referenztypen (Reference Types). Die Speichermechanismen beider Objektarten im Arbeitsspeicher unterscheiden sich vollständig.
1. Ein Werttyp belegt genau einen zusammenhängenden Speicherblock, in dem die Rohdaten direkt abgelegt werden.

2. Referenztypen benötigen zwei separate Speicherbereiche:
- Der erste Bereich enthält die echten Objektdaten und dieser Speicherplatz wird immer auf dem Heap reserviert;
- Der zweite Bereich ist eine Referenzadresse, die auf die Position der Daten im Heap zeigt. (Auf dem Stack wird die Heap-Adresse gespeichert; in der C-Sprache bezeichnete man dies als Zeiger)

Die beiden obigen Abbildungen gelten nur für eigenständige Objekte, die kein Datenmember eines anderen Objekts sind.
Hinweis
Der eigentliche Datenkörper jedes Referenztyp-Objekts befindet sich stets auf dem Heap, wie in den Abbildungen zu sehen.
Doch Instanzen von Werttypen oder Referenzadressen von Referenztypen können sowohl auf dem Stack als auch auf dem Heap liegen – abhängig von der Struktur des Quellcodes.
Beispiel:
Nehmen wir an, es gibt einen Referenztyp Person mit zwei internen Membern: einem Werttyp-Member und einem Referenztyp-Member.
Viele Entwickler gehen fälschlicherweise davon aus, dass Werttyp-Member auf dem Stack, Referenzadressen ebenfalls auf dem Stack und der Objektkörper auf dem Heap abgelegt werden (wie auf den Bildern), diese Annahme ist jedoch falsch.
Merken Sie sich eine zentrale Regel: Alle Rohdaten einer Referenztyp-Instanz werden dauerhaft auf dem Heap reserviert. Beide Member gehören zu den Daten dieses Objekts, also werden sie unabhängig davon, ob es sich um Wert- oder Referenztypen handelt, gemeinsam auf dem Heap abgelegt.
Code-Beispiel:
class Program
{
static void Main(string[] args)
{
int a = 10;
Person bill = new Person("Bill", 18);
}
}
class Person
{
private string name;
private int age;
private Phone myPhone;
public Person(string name, int age)
{
this.name = name;
this.age = age;
this.myPhone = new Phone();
this.myPhone.number = 12345;
}
}
class Phone
{
public long number;
}Code-Sprache: JavaScript (javascript)
Betrachten Sie das obige Beispiel: Die Klasse Person enthält die eingebauten Typen age und name sowie einen benutzerdefinierten Typ Phone. Der Typ Phone wiederum verfügt über den primitiven Typ long zur Speicherung von Handynummern.
Leser sehen vielleicht int age in Person oder long number in Phone und vermuten, dass diese Werttypen auf dem Stack gespeichert werden – das ist nicht der Fall. Obwohl es sich um eingebaute Basistypen handelt, werden sie als Member einer benutzerdefinierten Klasse zusammen mit der Objektinstanz auf dem Heap abgelegt. Die endgültige Speicheraufteilung sieht folgendermaßen aus:

Kernregel: Alle Datenmember von selbst definierten Klassen liegen auf dem Heap, egal ob es sich um Wert- oder Referenztypen handelt. Enthält eine Klasse einen Referenztyp-Member wie myPhone im Beispiel, speichert dieses Feld lediglich die Adresse eines anderen Objekts auf dem Heap.
Wenn die Klasse Phone einen weiteren benutzerdefinierten Typ enthielte, würde im Speicherbereich von Phone ein zusätzlicher Platz für die Referenzadresse dieses zweiten Objekts reserviert.
Das Programm beginnt seine Ausführung in der Main-Methode. Die Adresse des obersten Referenzobjekts liegt auf dem Stack, alle nachfolgenden Objekte hängen wie eine Kette im Heap zusammen.

Weiteres Beispiel
// Referenztyp-Klasse
class MyType
{
public int A; // Werttyp-Member, eingebettet in die MyType-Instanz auf dem Heap
public Person B; // Referenztyp-Member; der Referenzzeiger liegt auf dem Heap, die eigentlichen Daten erhalten einen separaten Heap-Bereich
}
class Person
{
public string Name;
}
void Test()
{
// Auf dem Stack wird der Referenzzeiger von myObj gespeichert
MyType myObj = new MyType();
/*
Heap-Aufbau:
MyType-Objekt {
A: inline gespeicherter int-Wert
B: Referenzzeiger auf die Person-Instanz
}
Separater Heap-Bereich: Person{ Name: ... }
*/
// Lokale Werttyp-Variable, direkt auf dem Stack reserviert
int localNum = 10;
}Code-Sprache: PHP (php)
Die oben genannten Regeln gelten ausschließlich für Datenmember der Klasse, nicht für deren Methoden. Deklariert eine Klassenmethode lokale Variablen oder Referenzobjekte, bleiben lokale Werttypen sowie die Adressen von Referenzinstanzen auf dem Stack gespeichert. Siehe folgenden Code:
Sehen Sie sich das folgende Beispiel an:
using System.ComponentModel.DataAnnotations;
class Program
{
static void Main(string[] args)
{
int a = 10;
Person bill = new Person("Bill", 18);
bill.Calculate();
}
}
class Person
{
private string name;
private int age;
private Phone myPhone;
public Person(string name, int age)
{
this.name = name;
this.age = age;
this.myPhone = new Phone();
this.myPhone.number = 12345;
}
public int Calculate()
{
int one = 12;
int two = 23;
Phone myNewPhone = new Phone();
return one + two;
}
}
class Phone
{
public long number;
}Code-Sprache: PHP (php)
In diesem Code habe ich der Klasse Person eine Calculate-Methode hinzugefügt. Innerhalb der Methode sind lokale Variablen sowohl von Wert- als auch von Referenztypen definiert, die aus Main heraus aufgerufen werden. Diese Variablen sind keine Member von Person, sondern methodenspezifische lokale Variablen: lokale Werttypen liegen auf dem Stack, die Referenzadressen lokaler Referenzobjekte ebenfalls.

Bei Ausführung der Zeile bill.Calculate(); ergibt sich folgende Speicheraufteilung

Der blaue Bereich auf der Abbildung zeigt den Speicherort der internen Variablen während der Methodenausführung.
Sobald die Methode die Ausführung beendet, werden alle diese lokalen Variablen vom Stack entfernt, die Speicherstruktur sieht dann so aus:

Nach Abschluss von Calculate kehrt der Programmfluss zurück nach Main, alle innerhalb der Methode erstellten lokalen Variablen werden vom Stack gelöscht. Der eigentliche Datenkörper von myNewPhone (der blaue Bereich auf der Abbildung) verbleibt jedoch auf dem Heap. Nur die Referenzadresse wird vom Stack entfernt; die echten Daten werden nicht sofort gelöscht, sondern bleiben auf dem Heap, bis der Garbage Collector GC sie zu einem späteren Zeitpunkt freigibt.
Werttypen und Referenztypen