Grundlegendes zur Speicherzuordnung in Delphi

Rufen Sie die Funktion "DoStackOverflow" einmal aus Ihrem Code auf und Sie erhalten die EStackOverflow Fehler von Delphi mit der Meldung "Stack Overflow".


 Funktion DoStackOverflow: integer;

Start

 Ergebnis: = 1 + DoStackOverflow;

Ende;

Was ist dieser "Stapel" und warum gibt es dort einen Überlauf mit dem obigen Code?

Die DoStackOverflow-Funktion ruft sich also rekursiv selbst auf - ohne eine "Exit-Strategie" - sie dreht sich einfach weiter und wird nie beendet.

Eine schnelle Lösung besteht darin, den offensichtlichen Fehler zu beseitigen und sicherzustellen, dass die Funktion zu einem bestimmten Zeitpunkt vorhanden ist (sodass der Code an der Stelle ausgeführt werden kann, an der Sie die Funktion aufgerufen haben)..

Du gehst weiter und schaust niemals zurück, ohne Rücksicht auf den Bug / die Ausnahme, wie er / sie jetzt behoben ist.

Die Frage bleibt jedoch: Was ist dieser Stapel und warum gibt es einen Überlauf?

Speicher in Ihren Delphi-Anwendungen

Wenn Sie mit dem Programmieren in Delphi beginnen, kann es vorkommen, dass ein Fehler wie der oben beschriebene auftritt. Sie können ihn beheben und fortfahren. Dieser hängt mit der Speicherzuordnung zusammen. Die meiste Zeit interessiert Sie die Speicherzuweisung nicht, solange Sie die von Ihnen erstellten Inhalte freigeben.

Wenn Sie mehr Erfahrung in Delphi sammeln, erstellen Sie Ihre eigenen Klassen, instanziieren sie, kümmern sich um die Speicherverwaltung und ähnliches.

Sie werden an den Punkt gelangen, an dem Sie in der Hilfe so etwas wie lesen werden "Lokale Variablen (in Prozeduren und Funktionen deklariert) befinden sich in einer Anwendung Stapel." und auch Klassen sind Referenztypen, daher werden sie bei der Zuweisung nicht kopiert, sie werden als Referenz übergeben und sie werden auf der Haufen.

Also, was ist "Stapel" und was ist "Haufen"?

Stapel gegen Haufen

Wenn Sie Ihre Anwendung unter Windows ausführen, befinden sich drei Bereiche im Speicher, in denen Ihre Anwendung Daten speichert: globaler Speicher, Heap und Stapel.

Globale Variablen (ihre Werte / Daten) werden im globalen Speicher gespeichert. Der Speicher für globale Variablen wird von Ihrer Anwendung beim Programmstart reserviert und bleibt so lange reserviert, bis Ihr Programm beendet wird. Der Speicher für globale Variablen heißt "Datensegment".

Da der globale Speicher bei Programmbeendigung nur einmal reserviert und freigegeben wird, ist es uns in diesem Artikel egal.

In Stack und Heap findet die dynamische Speicherzuweisung statt: Wenn Sie eine Variable für eine Funktion erstellen, wenn Sie eine Instanz einer Klasse erstellen, wenn Sie Parameter an eine Funktion senden und deren Ergebniswert verwenden / übergeben.

Was ist Stapel?

Wenn Sie eine Variable innerhalb einer Funktion deklarieren, wird der zum Speichern der Variablen erforderliche Speicher vom Stapel zugewiesen. Sie schreiben einfach "var x: integer", verwenden "x" in Ihrer Funktion und wenn die Funktion beendet wird, kümmern Sie sich nicht um die Speicherzuordnung oder die Freigabe. Wenn die Variable den Gültigkeitsbereich verlässt (Code verlässt die Funktion), wird der auf dem Stapel belegte Speicher freigegeben.

Der Stapelspeicher wird dynamisch mit dem LIFO-Ansatz ("last in first out") zugewiesen.

In Delphi-Programmen wird der Stapelspeicher von verwendet

  • Lokale Routinevariablen (Methode, Prozedur, Funktion).
  • Routineparameter und Rückgabetypen.
  • Windows API Funktionsaufrufe.
  • Datensätze (daher müssen Sie keine Instanz eines Datensatztyps explizit erstellen).

Sie müssen den Speicher auf dem Stapel nicht explizit freigeben, da der Speicher automatisch für Sie reserviert wird, wenn Sie beispielsweise eine lokale Variable für eine Funktion deklarieren. Wenn die Funktion beendet wird (manchmal sogar vorher aufgrund der Delphi-Compiler-Optimierung), wird der Speicher für die Variable automatisch freigegeben.

Die Größe des Stapelspeichers ist standardmäßig groß genug für Ihre (so komplexen wie sie sind) Delphi-Programme. Die Werte "Maximale Stapelgröße" und "Minimale Stapelgröße" in den Linker-Optionen für Ihr Projekt geben Standardwerte an - in 99,99% müssten Sie dies nicht ändern.

Stellen Sie sich einen Stapel als einen Stapel von Speicherblöcken vor. Wenn Sie eine lokale Variable deklarieren / verwenden, wählt der Delphi-Speichermanager den Block von oben aus und verwendet ihn. Wenn er nicht mehr benötigt wird, wird er zurück in den Stapelspeicher verschoben.

Wenn der lokale Variablenspeicher vom Stapel verwendet wird, werden lokale Variablen bei der Deklaration nicht initialisiert. Deklarieren Sie eine Variable "var x: integer" in einer Funktion und versuchen Sie einfach, den Wert zu lesen, wenn Sie die Funktion eingeben - x wird einen "seltsamen" Wert ungleich Null haben. Initialisieren (oder setzen) Sie daher immer Ihre lokalen Variablen, bevor Sie deren Wert lesen.

Aufgrund von LIFO sind Stapeloperationen (Speicherzuweisung) schnell, da nur wenige Operationen (Push, Pop) erforderlich sind, um einen Stapel zu verwalten.

Was ist Haufen?

Ein Heap ist ein Speicherbereich, in dem dynamisch zugewiesener Speicher gespeichert wird. Wenn Sie eine Instanz einer Klasse erstellen, wird der Speicher aus dem Heap zugewiesen.

In Delphi-Programmen wird der Heapspeicher von / when verwendet

  • Instanz einer Klasse erstellen.
  • Erstellen und Ändern der Größe von dynamischen Arrays.
  • Explizite Speicherzuweisung mit GetMem, FreeMem, New und Dispose ().
  • Verwendung von ANSI / Wide / Unicode-Strings, Varianten, Interfaces (automatisch von Delphi verwaltet).

Heap-Speicher hat kein nettes Layout, bei dem es eine gewisse Reihenfolge geben würde, Speicherblöcke zuzuweisen. Haufen sieht aus wie eine Dose Murmeln. Die Speicherzuweisung vom Heap ist zufällig, ein Block von hier als ein Block von dort. Daher sind Heap-Operationen etwas langsamer als die auf dem Stapel.

Wenn Sie nach einem neuen Speicherblock fragen (d. H. Eine Instanz einer Klasse erstellen), übernimmt Delphi Memory Manager dies für Sie: Sie erhalten einen neuen oder einen gebrauchten und verworfenen Speicherblock.

Der Heap besteht aus dem gesamten virtuellen Speicher (RAM und Speicherplatz).

Manuelles Zuweisen von Speicher

Jetzt, da alles über den Speicher klar ist, können Sie (in den meisten Fällen) das Obige ignorieren und einfach weiter Delphi-Programme schreiben, wie Sie es gestern getan haben.

Natürlich sollten Sie wissen, wann und wie Sie Speicher manuell zuweisen / freigeben.

Der "EStackOverflow" (vom Anfang des Artikels an) wurde ausgelöst, weil bei jedem Aufruf von DoStackOverflow ein neues Speichersegment aus dem Stapel verwendet wurde und der Stapel Einschränkungen aufweist. So einfach ist das.