Einzelnen Beitrag anzeigen

peterbelow

Registriert seit: 12. Jan 2019
Ort: Hessen
670 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: Speicherverwaltung bei Basisdatentypen

  Alt 13. Mär 2019, 12:49
Ich habe mal eine grundsätzliche Frage, was ich einfach noch nicht verstehe.

Wenn ich ein Objekt erzeuge wird ja der Speicher reserviert und erst wieder zum erneuten beschreiben freigegeben, wenn ich es wieder Freigebe.

Wie Funktioniert das eigentlich bei den Basisdatentypen? Woher weiß Delphi wann der Speicher wieder freigegeben werden kann? Bei Lokalen Variablen vermutlich wenn sie den Gültigkeitsbereich verlassen... aber wie ist das mit Objektvariablen? Geben Objekte automatisch den Speicher ihrer Variablen frei, wenn sie zerstört werden? Und wie ist das bei globalen Variablen?

Ich weiß... doofe frage, aber irgendwie interessiert mich das
Wie Michael in seiner Antwort schon sagte liegen lokale Variablen (in einer Methode/procedure/function deklariert) auf dem Call-Stack und werden damit (zumindest für Value-Typen) automatisch "abgeräumt" wenn die Methode verlassen wird. Für Reference-Typen enthalten die lokalen Variablen aber nur Pointer auf anderswo angelegten Speicher und der Compiler verwaltet diesen Speicher nur für einige besondere Typen automatisch (normalerweise per reference-counting). Das sind insbesondere String, Ansistring, Widestring, Interface-Typen, dynamische Arrays, anonyme Methoden. Was Du selbst im Code an Objekten oder Heap memory anlegst und in lokalen Variablen ablegst must Du auch am Ende der Methode selbst wieder freigeben (Stichwort try ... finally), sonst hast Du ein Speicherleck.

Für Variablen (Felder) in einem Objekt gilt Ähnliches: Das Objekt selbst bekommt, wenn es erzeugt wird, einen Speicherblock vom Memory manager, der den Platz für alle Felder enthält (und noch einiges mehr). Wenn das Objekt zerstört wird geht der Block an den Memory manager zurück und vorher werden auch für alle Felder, die compiler-managed reference types enthalten (siehe oben) die reference counts um eins vermindert und damit eventuell der Speicherblock, auf den die Referenz verweist, freigegen. Für andere reference types (Objekte, pointer auf Heap-Memory) gilt das vorstehend gesagte: was Du selbst angelegt hast (normalerweise im Constructor der Klasse) mußt Du auch selbst freigeben (normalerweise im Destructor der Klasse), sonst gibt es ein memory leak. Kompliziert wird's, wenn für erzeugte Objekte Referenzen an verschiedenen Stellen im Kode existieren, da muss man sich sehr genau überlegen, wer nun für die Verwaltung der Lebensdauer veranwortlich ist.

Für globale Variablen gibt es ein data segment im EXE selbst, das aus der Datei in den Speicher geladen wird und eine feste Größe hat. Dieser Speicher existiert solange die Anwendung läuft; Variablen, die nicht explizit in der Deklaration initialisiert wurden, sind auf 0 bzw. nil gesetzt. Globale Variablen in Units, die compiler-managed types enthalten, werden im Finalization-Kode der Unit freigegeben (über den reference count). Der Compiler erzeugt dabei automatisch die Finalization section, wenn der Programmier nicht explizit eine angegeben hat. Für andere reference types gilt wieder: was man selbst angelegt hat (normalerweise in einer Initialization section) muss man auch wieder freigeben (normalerweise in einer Finalization section).

Komponenten (alles, was von TComponent) abstammt) machen die Sache noch undurchsichtiger, da diese Klasse einen Mechanismus für die Verwaltung der Lebensdauer per "ownership" enthält. Wenn man (oder die VCL beim Laden eines DFM-Files) eine Komponente mit einem Owner anlegt wird sie automatisch zerstört, wenn der Owner ablebt. Da alle autocreated Forms und Datamodules das Application-Objekt als Owner haben und dieses von der VCL explizit in der Finalization-Sektion zerstört wird braucht man sich um die von der IDE erzeugten Form-Variablen gemeinhin keine Gedanken machen. Für TControls gilt dazu noch: wenn der Parent das Zeitliche segnet bringt er vorher noch alle seine Kinder um, was normalerweise eine fette Schlagzeile in der Bildzeitung wert wäre.

Eine Sache sollte man aber immer im Hinterkopf behalten: wenn man ein Objekt zerstört (seine Free-Methode aufruft) oder einen Heap-Pointer freigibt (Dispose, FreeMem etc.) wird dadurch die Variable, in der die referenz abgelegt wurde, [B]nicht[B] automatisch auf nil gesetzt! Wenn man die Referenz dann später versehendlich nochmal verwendet hat das oft seltsame Fehler zur Folge...
Peter Below
  Mit Zitat antworten Zitat