Olaf Lischke
.NET C#
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.

Postgresql
Mehrere PostgreSQL-Instanzen auf einem Rechner

Mehrere PostgreSQL-Instanzen auf einem Rechner

Es kann sehr nützlich sein, mehrere Instanzen eines Datenbanksystems auf einer Maschine zu betreiben, und sei es nur, um in sehr kleinem Umfeld Produktion und Entwicklung von einander getrennt zu halten.

Wer Microsofts SQL Server benutzt, startet einfach den Installer neu und legt eine weitere Instanz an. PostgreSQL geht hier einen eigenen Weg: Die von einer Instanz verwalteten Datenbanken bezeichnet PostgreSQL als Database Cluster, und so ein Cluster ist nichts anderes als ein weiteres Datenverzeichnis – das jedoch mit einem eigenen Dienst und zugehörigen Port ausgestattet werden will.

Die Schritte in Kürze:

  • Anlegen des neuen Datenverzeichnisses (initdb)
  • Konfigurieren des Ports (postgres.conf)
  • Initialisieren des Dienstes (pg_ctl)
  • Dienst starten

Wir starten mit dem Anlegen des Datenverzeichnisses. Dazu braucht der verwendete Benutzer die Rechte zum Anlegen neuer Verzeichnisse sowie zum Verwalten von Diensten, am besten ein (lokaler) Admin. In einer Eingabeaufforderung/Powershell benutzen wir dafür initdb aus den Postgres-Binaries:

"C:\Program Files\PostgreSQL\[version]\bin\initdb" -D "C:\Data\pgdata2"

Der Switch -D erwartet die Angabe des gewünschten Datenverzeichnisses (hier C:\Data\pgdata2). An dieser Stelle das Installationsverzeichnis von PostgreSQL anzugeben, ist keine gute Idee, besser, man legt ein eigenes Verzeichnis an, wenn nicht schon längst geschehen.

InitDb macht genau das, was der Name suggeriert: Es legt im angegebenen Verzeichnis alles an, was PostgreSQL benötigt:

Damit dieser Cluster erreichbar wird, benötigt er die Angabe eines Ports in der postgresql.conf im gerade angelegten Verzeichnis: Wir öffnen also C:\Data\pgdata2\postgresql.conf mit einem Editor unserer Wahl und tragen einen Port ein, der noch nicht verwendet wird:

Vor dem Speichern ggf. noch schnell den Eintrag listen_addresses einkommentieren, sonst kommen wir trotz aller Portangaben nicht auf die Instanz. Da der zugehörige Dienst noch nicht existiert, können wir die Kommentare zum Neustart ignorieren.

Als nächstes starten wir den passenden Server: Initialisieren des Serverdienstes mit pg_ctl – in einer Eingabeaufforderung/Powershell mit entsprechenden Rechten

pg_ctl register -N <Name des neuen Postgres-Dienstes> -D <Datenverzeichnis>

Also zum Beispiel

pg_ctl register -N PostgresDevService -D "D:\Data\pgdata2"

Der Switch -N will den Namen des neuen Dienstes, -D gibt wieder das Datenverzeichnis an.

War das erfolgreich, können wir den Dienst starten:

net start PostgresDevService

Der Datenbank-User für Verbindungen zu dieser neuen Instanz ist zunächt der Windows-User, unter dem wir dies alles gerade durchgeführt haben (also nicht postgres!), das Passwort entsprechend das dazugehörige Windows Passwort. Wer hier mehr/anderes braucht, kann nun in der neuen Instanz Rollen und Rechte anlegen, zuordnen und verwalten (siehe PostgreSQL-Doku).

Tipp bei Verbindungsproblemen, insb. remote: pg_hba.conf regelt, von welchen IP-Adressen wie zugegriffen werden darf (PostgreSQL Doku), und die Windows-Firewall redet natürlich auch noch ein Wörtchen mit…