Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi prüfen, ob dyn. zur Runtime erzeugte Form Speicher freigibt? (https://www.delphipraxis.net/16943-pruefen-ob-dyn-zur-runtime-erzeugte-form-speicher-freigibt.html)

Hansa 25. Feb 2004 23:44


prüfen, ob dyn. zur Runtime erzeugte Form Speicher freigibt?
 
Eine Form wird zur Laufzeit erzeugt, mit

Delphi-Quellcode:
MyForm := TMyForm.Create (self);
Jetzt will ich wissen, ob der Speicher tatsächlich freigegeben wird, sofern ich die Form schließe. Ich bräuchte also für den Fall der Fälle (auch für anderes) den momentan freien Speicher.

Luckie 25. Feb 2004 23:59

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Kuck dir mal GetHeapStatus an. Damit kannst du selber etwas bauen. Oder es gibt für Delphi auch so Experts mit denen man Memory-Leaks aufspüren kann. Mir fällt leider im Moment keiner ein.

Luckie 26. Feb 2004 00:30

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Ich noch mal. Also kucken wir mal:
Delphi-Quellcode:
var
  NewFormModal, NewFormNotModal: TForm;

procedure TForm1.Button1Click(Sender: TObject);
begin
  NewFormModal := TForm2.Create(self);
  try
    NewFormModal.ShowModal;
  finally
    FreeAndNil(NewFormModal);
  end;
end;
Hier wird der Speicher von Form2 bestimmt wieder freigegeben.

Anders sieht es hier aus:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
begin
  NewFormNotModal := TForm2.Create(self);
  NewFormNotModal.Show;
end;
Jetzt ist die Frage, ob das Close in Form zwei die Form auch wieder aus dem Speicher schmeißt.

Testen wir das mal:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
begin
  if not Assigned(NewFormNotModal) then
  begin
    NewFormNotModal := TForm2.Create(self);
    NewFormNotModal.Show;
  end;
end;
Form2 wird hier nur erzeugt, wenn die Objektvariable nicht nil ist. Folglich kann man die neue Form auch nur einmal erzeugen. Jetzt der Test, schließt man Form2 mit Close, kann liefert Assigned immer noch true.

Daraus lassen sich jetzt zwei Schlüsse ziehen: Entweder wird der Speicher freigegeben und der Zeiger nicht auf Nil gesetzt. Was ich für unwahrscheinlich halte. Ich denke, die Borländer hätten dann auch den Zeiger genillt. Oder aber der Speicher wird nicht wieder freigegeben.

Selbst ein
Delphi-Quellcode:
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;
im OnClose der Form2 ändert nichts an obigen verhalten.

Delphi-Quellcode:
procedure TForm2.Button1Click(Sender: TObject);
begin
  Close;
  FreeAndNil(Unit1.NewFormNotModal);
end;
Endet mit einer AccessViolation.

So weit meine Untersuchungen. Leider bin ich zu keinem vernünftigem Ergebnisgekommen. :?

Robert_G 26. Feb 2004 00:39

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Als Hansa seinen 2. (oder war's schon der 3. :gruebel: ) Thread zum Thema dyn. Forms aufgemacht hat, habe ich mir ein Form ertellt, lauter Komponenten draufgezogen (ListView, SynMemo, Memo, Edit,...) + 2 Buttons.
Der erste erzeuge 500 Instanzen von der Form, der 2. hat sie wieder entfernt.
Vorher waren es 7 MB Speichernutzung, mit 501 Forms etwa 50MB und nach dem Löschen etwa 9MB.
Ich habe zum entfernen der Forms "Close" verwendet und im OnClose stand "Action := caFree".

jbg 26. Feb 2004 00:43

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Zitat:

Zitat von Luckie
Ich denke, die Borländer hätten dann auch den Zeiger genillt.

Und wie hätten die Borländer das anstellen sollen? Woher soll die Instanz denn wissen wieviele Referenzen denn auf sie zeigen?

Luckie 26. Feb 2004 00:45

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Stimmt auch wieder. Und wie sieht es nun aus? Meine Testreihe ist ja damit hinfällig.

jbg 26. Feb 2004 00:50

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Zitat:

Zitat von Robert_G
Vorher waren es 7 MB Speichernutzung, mit 501 Forms etwa 50MB und nach dem Löschen etwa 9MB.

Für den Unterschied gibt es zwei mögliche Erklärungen:
  • Es wurde Code ausgeführt, der Speicher reserviert, der erst wieder beim Beenden (also von TApplication.Destroy) freigegeben wird.
  • Der Delphi-Speichermanager hält freigegebenen Speicher zurück um ihn um Welten schneller an eine Speicherallozierung (GetMem/AllocMem/New/...) zu liefern, als es Windows je könnte. Somit bekommt Windows natürlich nichts von der Freigabe mit und zeigt im Taskmanager einen höheren Verbrauch an, auch wenn der Speicher für das Programm "frei" ist.
    Das ist auch der Grund für den enormen Speicherverlust beim zeichenweise Zusammenstückeln von Strings und dynamischen Arrays in Schleifen.

Luckie 26. Feb 2004 01:05

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
So. Habe es getestet. das:
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
begin
  NewFormNotModal := TForm2.Create(self);
  NewFormNotModal.Show;
end;
hinterläßt ein Speicherleck. TForm.Close gibt also keinen Speicher frei.

Leuselator 26. Feb 2004 01:10

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
war mir so als ob es so sei, dass wenn man irgendwo "Create" schreibt, man immer auch "Free" bzw. "FreeAndNil" schreiben muß (sogenannte "Wer A sagt muß auch B sagen - Regel") ?

Luckie 26. Feb 2004 01:13

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Nur wo willst du ein nicht modales Form freigeben? In der gleichen Prozedur geht nicht, dann wird es erst gar nicht angezeigt. Also wo?

Leuselator 26. Feb 2004 01:54

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Vorab: kann sein, das ich hier voll am Thema vorbeischlitter.

Ich mache es mit Formularen, die ich öfter benötige so:
Code:
type
  TMyComponentForm = Class(TPersistent)
  private
    FFenster : TMySpecialForm;
  public
    Constructor Create(AOwner : TComponent); override;
    Destructor Destroy; override;
    procedure Show;
    function ShowModal : TModalResult;
    [color=red]procedure Hide;[/color]
  end;

procedure Register;

implementation
uses U_MySpecialForm; //Unit des Forms

procedure Register;
begin
  RegisterComponents('LS StandardForms', [TMyComponentForm]);
end;

Constructor TMyComponentForm.Create(AOwner:TComponent);
begin
  inherited Create(AOwner);
  FFenster := TMySpecialForm.Create(self);
end;

procedure TMyComponentForm.Show;
begin
  FFenster.Show;
end;

function [color=red]TMyComponentForm.[/color]ShowModal : TModalResult;
begin
  Result := FFenster.ShowModal;
end;

[color=red]procedure TMyComponentForm.Hide;
begin
  FFenster.Hide;
end;[/color]
Destructor TMyComponentForm.Destroy;
begin
  FreeAndNil(FFenster);
  inherited;
end;
Das ergibt mit wenig Arbeit eine Komponente, die ich auf meinem MainForm plaziere, und die das Form auf jeden Fall beim Beenden der Application wieder freigibt.

War sowas gesucht?
Gruß
(Rot = Edit)

jbg 26. Feb 2004 10:10

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Zitat:

Zitat von Leuselator
Das ergibt mit wenig Arbeit eine Komponente, die ich auf meinem MainForm plaziere, und die das Form auf jeden Fall beim Beenden der Application wieder freigibt.

Und ich vergebe einfach einen Owner beim Erzeugen, dann kümmert sich die VCL um die Freigabe beim Beenden bzw. wenn die Owner-Komponente freigegeben wird, werden alle "besessenen" Komponenten in einer Schleife in TComponent.Destroy freigegeben.

roderich 26. Feb 2004 10:47

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Zitat:

So. Habe es getestet. das:
Source:

procedure TForm1.Button2Click(Sender: TObject);
begin
NewFormNotModal := TForm2.Create(self);
NewFormNotModal.Show;
end;


hinterläßt ein Speicherleck. TForm.Close gibt also keinen Speicher frei.
Natürlich ist das kein Speicherleck, wieso sollte TForm.Close Speicher freigeben ???
Close setzt einfach nur Visible auf false, z.B. kann man dieses Form mit Show später wieder anzeigen.

Roderich

Hansa 26. Feb 2004 11:48

Re: prüfen, ob dyn. zur Runtime erzeugte Form Speicher freig
 
Meine Testreihe ist beendet, mit folgendem Ergebnis :

close alleine gibt den Speicher nicht automatisch frei. Ebensowenig wie das :
Delphi-Quellcode:
Action := caFree;
im OnClose. Zusätzlich verzögert sich die Laufzeit schon bemerkbar. Setze ich ein free oder Destroy hinter das Create, so wurden bisher 1,1 Mio Forms erzeugt und wieder freigegeben. Die Laufzeit für jede erzeugte Form war gleich. Das ist nun Schade, denn das nützt mir nichts.

Ich deklariere die Forms nämlich so :

TMeineForm = class (TForm)
..

TDeineForm = class (TMeineForm)
..

usw.

dementsprechend erzeuge ich sie so :
Delphi-Quellcode:
procedure TMeineForm.lblDeineFormClick(Sender: TObject);
begin
  inherited;
  DeineForm:= TDeineForm.Create(self);
  DeineForm.Show;
end;
Hintergrund ist der, daß ich viele Eigenschaften schon in TMeineForm deklariere. Die Schriftart, einige Steuerelemente, einige Eigenschaften. Diese Form lege ich in das Repository/Objektablage und sage nicht neu -> Form, sonder neu -> weitere -> Inherited. Somit ist alles schon überall richtig und gleich eingestellt. Nun habe ich gestern ein Panel vergessen, das auf jede Form noch drauf mußte. Anstatt die ganzen Forms einzeln umzubauen, habe ich das auf MeineForm gelegt und dank OOP war es auf allen anderen Forms drauf.

Wenn ich nun aber die Forms dynamisch erzeuge, so bräuchte ich analog eine zentrale Stelle, um sie wieder zu entfernen. Leider geht das mit caFree im OnClose eben nicht.

Ich sehe gerade, daß ich weder Constructor noch destructor habe. :gruebel:


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:43 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz