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 FastIniFiles Memory Leaks (https://www.delphipraxis.net/143732-fastinifiles-memory-leaks.html)

API 22. Nov 2009 07:14


FastIniFiles Memory Leaks
 
Hallo

Bin auf FastIniFiles (http://home.arcor.de/smartprog4u/) umgestiegen weil es ein paar nette Funktionen bietet und schneller läuft.
Das Problem: Beim Beenden des Programms gibt es Memory Leaks.

Testcode:

Delphi-Quellcode:
uses
 FastIniFiles;

procedure TForm1.FormCreate(Sender: TObject);
begin
  with TFIniFile.Create('C:\...\test.ini') do
  try
  (*........*)
  finally
    Free;
  end;
end;
Leaks:

Zitat:

----------------------------------------------------------------------------
|+Memory Leak: Type=Data; Total size=32; Count=2 |
|--------------------------------------------------------------------------|
|004AD2E6|Project1.exe|FastIniFiles.pas|TFIniFile| Create |346[92] |
|004AD2FC|Project1.exe|FastIniFiles.pas|TFIniFile| Create |347[93] |
|--------------------------------------------------------------------------|
| |
|+Memory Leak: Type=TFIniValue; Total size=32; Count=2 |
|--------------------------------------------------------------------------|
|004AD361|Project1.exe|FastIniFiles.pas|TFIniFile| Create |354[100]|
|004ACF13|Project1.exe|FastIniFiles.pas|TFIniFile| Create |243[9] |
----------------------------------------------------------------------------
Ein Leak soll gem. Anzeige oben hier auftreten:
Delphi-Quellcode:
constructor TFIniFile.Create(const FileName: string; CommentChar: Char = '#');
var
  Stream: TStream;
begin
  //...
  Self := Create(Stream);
  Stream.Free;
end;
Warum denn? Das Ini File wird ja wieder freigegeben und sobit der Stream auch??

Zudem gibt es noch etliche andere Leaks. Vermutlich in den Destruktoren. Wer findet noch welche?

Genügt z.B das Finalize oder braucht es noch ein for i := Low(Values) to High(Values) do Values[i].Free; ?

Delphi-Quellcode:
destructor TFIniSection.Free;
var
 i: Integer;
begin
  Name := '';
  Finalize(Values);
  inherited Free;
end;

Chemiker 22. Nov 2009 08:29

Re: FastIniFiles Memory Leaks
 
Hallo API,

ich kenne jetzt nicht FastIniFiles, aber ich könnte mir vorstellen wenn es sich um eine Class handelt, dass man sie nicht mit free freigibt, sondern mit destory.

Bis bald Chemiker

jfheins 22. Nov 2009 09:17

Re: FastIniFiles Memory Leaks
 
.Free ist ein Prozedur, die auf <>nil prüft und dann destroy aufruft, .Free ist also mindestens equivalent, wenn nicht gar vorzuziehen ggü. destroy

hoika 22. Nov 2009 09:45

Re: FastIniFiles Memory Leaks
 
Hallo,

wer programmiert denn so schlimm ;)

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  FIniFile: TFIniFile;
begin
  FIniFile:= TFIniFile.Create('C:\...\test.ini');
  try
  finally
    FIniFile.Free;
  end;
end;
oder mit dem with
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin
  with TFIniFile.Create('C:\...\test.ini') do
  begin
    try
    (*........*)
    finally
      Free;
    end;
  end;
end;
Schönes Bsp., wie man sich mit with Probleme bereiten kann
(liegt aber nicht am with, sondern an der Programmierung).

Delphi-Quellcode:
constructor TFIniFile.Create(const FileName: string; CommentChar: Char = '#');
var
  Stream: TStream;
begin
  //...
  Create(Stream);
  Stream.Free;
end;

Heiko

jfheins 22. Nov 2009 09:49

Re: FastIniFiles Memory Leaks
 
Ich glaube eher es liegt hier: Self := Create(Stream);

Das passiert eigentlich implizit im constructor - mit anderen Worten: Es wird ein Objekt erstellt, an Self zugewiesen und dann der Kontruktor aufgerufen. Wenn der jetzt noch ein Objekt erstellt und wieder an Self zuweist dann geht jede Referenz auf das erste verloren, aber der Speicher wird nicht freigegeben.

API 22. Nov 2009 10:08

Re: FastIniFiles Memory Leaks
 
Zitat:

Zitat von jfheins
Ich glaube eher es liegt hier: Self := Create(Stream);

Das passiert eigentlich implizit im constructor - mit anderen Worten: Es wird ein Objekt erstellt, an Self zugewiesen und dann der Kontruktor aufgerufen. Wenn der jetzt noch ein Objekt erstellt und wieder an Self zuweist dann geht jede Referenz auf das erste verloren, aber der Speicher wird nicht freigegeben.

Wie kann man das beheben?

@hoika: Was ist denn daran falsch?

Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  FIniFile: TFIniFile;
begin
  FIniFile:= TFIniFile.Create('C:\...\test.ini');
  try
  finally
    FIniFile.Free;
  end;
end;

hoika 22. Nov 2009 10:13

Re: FastIniFiles Memory Leaks
 
Hallo,


Delphi-Quellcode:
destructor TFIniSection.Free;
var
i: Integer;
begin
  Name := '';
  Finalize(Values);
  inherited Free;
end;
Delphi-Quellcode:
destructor TFIniSection.Destroy;
var
i: Integer;
begin
  Name := '';
  Finalize(Values);
  inherited Destroy;
end;



Heiko

himitsu 22. Nov 2009 10:21

Re: FastIniFiles Memory Leaks
 
Delphi-Quellcode:
destructor TObject.Destroy;
procedure TObject.Free;
Destroy ist der Destructor !

Und beim Destructor nie das Override vergessen !

API 22. Nov 2009 10:23

Re: FastIniFiles Memory Leaks
 
@hoika: Bei beiden Varianten wird der
destructor TFIniFile.Free(SaveChanges: Boolean = true); aufgerufen.

Zudem: Warum kann man das nicht mit with machen? Danach muss überall ein FIniFile. geschrieben werden, der Code wird unschön (/größer)

Delphi-Quellcode:
var
  FIniFile: TFIniFile;
begin
  FIniFile:= TFIniFile.Create('C:\...\test.ini');
  try
    if FIniFile.SectionExists('Section') then
    begin
      FIniFile.EnterSection;
      ... := FIniFile.ReadString('Value1');
    end;
    FIniFile....
    FIniFile.....
    FIniFile.... usw..
  finally
    FIniFile.Free;
  end;

API 22. Nov 2009 10:28

Re: FastIniFiles Memory Leaks
 
Zitat:

Zitat von himitsu
Delphi-Quellcode:
destructor TObject.Destroy;
procedure TObject.Free;
Destroy ist der Destructor !

Und beim Destructor nie das Override vergessen !

Nach hinzufügen des override kommt aber eine Fehlermeldung.

[Error] FastIniFiles.pas(77): Cannot override a static

@hoika: auch nach ändern durch inherited Destroy; geht der Leak nicht weg.

himitsu 22. Nov 2009 10:33

Re: FastIniFiles Memory Leaks
 
man kann das mit With machen, aber man sollte schon den richtigen Desktructor nutzen

Zitat:

Zitat von API
@hoika: auch nach ändern durch inherited Destroy; geht der Leak nicht weg.

man überschreibt nicht Free, sondern Destroy



Delphi-Quellcode:
destructor TFIniSection.Destroy; // in der Definition mit OVERRIDE;
var
  i: Integer;
begin
  Name := '';
  Finalize(Values);
  inherited;
end;

hoika 22. Nov 2009 10:40

Re: FastIniFiles Memory Leaks
 
Hallo,

Free wird zwar aufgerufen,
der Destructor heisst aber Destroy.

Mit dem with stimmt wie oben.

Was spricht gegeben

FIniFile:=
with FIniFile

Ausserdem verstehe ich nicht,
warum man heutzutage noch IniFiles benutzt ...


Heiko

hoika 22. Nov 2009 10:42

Re: FastIniFiles Memory Leaks
 
Hallo,

zeig doch noch mal den Leak-Code (fastmm).


Heiko

API 22. Nov 2009 11:08

Re: FastIniFiles Memory Leaks
 
Zitat:

Zitat von hoika
Hallo,
zeig doch noch mal den Leak-Code (fastmm).
Heiko

http://codepaste.net/78msi9

hoika 22. Nov 2009 11:40

Re: FastIniFiles Memory Leaks
 
Hallo,

hast du die unit überarbeitet (Free->Destroy override) ?

Der Programmierer der Unit hat die leaks "eingebaut".



Heiko

jfheins 22. Nov 2009 11:58

Re: FastIniFiles Memory Leaks
 
FastIniFile + MemeoryLeak :arrow: http://en.wikipedia.org/wiki/Space-time_tradeoff :mrgreen:

Klaus01 22. Nov 2009 12:10

Re: FastIniFiles Memory Leaks
 
Delphi-Quellcode:
  TFIniEntry = class
    public
      // Markiert den Wert zum löschen, d.h. er wird nicht geschrieben
      Deleted: Boolean;
      constructor Create;
  end;

  TFIniFile = class
    private
      // Sektionen in der Datei
      Entries: array of TFIniEntry;
      // Index der geöffneten Sektion
      CurrentSection: Integer;
Delphi-Quellcode:
destructor TFIniFile.Free(SaveChanges: Boolean = true);
begin
  if isChanged and SaveChanges then Save;
  Finalize(Entries);
  inherited Destroy;
end;
Ich denke, dass Finalize nur das dyn Array dereferenziert aber
nicht die Instanzen von TFIniEntry freigibt.
Dazu müßte vorher durch das Array iteriert werden und die Instanzen
freigeben werden.

Grüße
Klaus

hoika 22. Nov 2009 12:17

Re: FastIniFiles Memory Leaks
 
Hallo,

Einspruch.

Lese ich mir die Hilfe zu Finalize durch,
macht sie genau so, was sie soll.

Aber ein
Entries.Dispose;

geht hier doch auch ?

Noch mal die Frage an den TE,
hast du Free durch Destroy ersetzt ?


Heiko

hoika 22. Nov 2009 12:33

Re: FastIniFiles Memory Leaks
 
Hallo,

Die Memleaks sind bei

1.
Delphi-Quellcode:
SetLength(Values, Length(Values)+1);
Values[High(Values)] := TFIniValue.Create(Buf1, Buf2);
und 2. bei
Delphi-Quellcode:
SetLength(Entries, Length(Entries)+1);
Entries[High(Entries)] := TFIniSection.Create(Buf);
Warum, darf der Programmierer selber rausfinden.
Ich klink mich aus.


Heiko

himitsu 22. Nov 2009 13:01

Re: FastIniFiles Memory Leaks
 
Joar, man könnte auch die schonmal gestellte Frage aufwerfen:
Warum unbedingt INI?

PS: TMemIniFile und andere schnellere Ini-Varianten gibt es notfalls auch noch.

PSS: Finalize gibt nur das Array, aber nicht die darin enthaltenen Objekte frei.
Finalize gibt allgemein keine Objekte/Pointer frei, sondern nur Strings, DynArrays und Interfaces.

Und wenn ich mir diese Unit so ansehe, dann werden alle intern erstellten Objekte niemals freigegeben.

alzaimar 22. Nov 2009 13:10

Re: FastIniFiles Memory Leaks
 
Zitat:

Zitat von hoika
Einspruch.
Lese ich mir die Hilfe zu Finalize durch,
macht sie genau so, was sie soll.
Aber ein
Entries.Dispose;
geht hier doch auch ?

Noch mal die Frage an den TE,
hast du Free durch Destroy ersetzt ?

1. Finalize(Array) fässt keine Zeiger an, die in dem Array abgelegt sind. Das steht so im Quellcode. Finalize hier aufzurufen, ist auch überflüssig, weil das Delphi macht. Wenn man nicht mit Referenzzählern rumspielt, muss man nie Finalize selbst aufrufen.

2. 'Entries.Dispose' geht nicht, weil 'Entries' kein Zeiger ist (nun, eigentlich schon, aber nicht offiziell)

3. Free ist nur sicherer als destroy, macht aber nicht mehr. Also würde es ein Speicherleck nicht vermeiden.

Wenn in dem dynamischen Array wirklich Klassen abgelegt sind, dann muss man die einzelnen Elemente auch extra freigeben. Oder man verwendet eine TObjectList.

himitsu 22. Nov 2009 13:19

Re: FastIniFiles Memory Leaks
 
Mein Fazit zu dieser Klasse:

Solange wie der Ersteller diese nicht grundlegend überholt, ist sie absolut nicht empfehlenswert.

- das Chaos in den Destruktoren (Free ist kein Destructor)
- die Speicherlecks (fast keines der erstellen Objekte wird jemals freigegeben
- ab Delphi 2009 funktioniert keiner seiner ASM-Codes mehr,
da alles auf String/PChar/Char ausgelegt ist, was ab da ja wohl Unicode sein wird
und die ASM-Codes nur auf ANSI laufen. (wenn er wenigstens AnsiString/PAnsiChar und AnsiChar genommen hätte)

[add]
Die Entwicklerecke ist witzig ... warum bin ich da eigentlich in einem C#-Forum angemeldet ... bin doch nur ins Delphi-Foum eingetreten :shock:
http://www.c-sharp-forum.de/viewtopi...=585351#585351
http://www.delphi-forum.de/viewtopic...r=asc&start=60

nja, eigentlich wollte ich via Google nur einen "Kontakt" zum Ersteller suchen, da seine Webseite (siehe Link in der Unit) nicht mehr existiert und da bin ich doch tatsächlich auf C# gestoßen :nerd:

TDS 22. Nov 2009 17:51

Re: FastIniFiles Memory Leaks
 
Huhu,

ich bin ja derjenige der aktuell die Unit hostet. Ich habe die Lecks schon früher bemerkt, jedoch war mir das für ein kleines Programm egal.
Man benötigt solch eine Unit wenn in der Ini-Datei entweder Leerzeichen in den Sektionen sind bzw. es doppelte Sektionen gibt. Da steigt das normale TIniFile und seine Abkömmlinge aus.

Wenn mir jemand erklärt wie ich die ASM-Blöcke rausbekomme und sie fit für UTF in Delphi 2009 mache, dann schreibe ich gern eine neue Version :-)

Gruß TDS

himitsu 22. Nov 2009 18:05

Re: FastIniFiles Memory Leaks
 
Wieso sollte man doppelte Sektionen in einer INI haben wollen?

Und wegen der ASM-Codes:
- man könnte diese für Unicode umbauen (1-Byte auf 2-Byte), aber dann laufen sie nicht mehr mit Ansi
oder man hat alles Doppelt und direkt die einzelnen Befehle entsprechend über Compilerschalter umschalten wäre noch unübersichtlicher, da es zuviele Stellen sind
- oder ... nja, ich hab einfach die ASM-Codes entfernt
> z.B. BinaryToHex durch Delphis BinToHex ersetzt und die restlichen Codes durch 'ne Pascal-Version
(ich hoffe mal diese Versionen laufen auch)

Ergebnis siehe anderer Thread (im DF)

TDS 22. Nov 2009 18:28

Re: FastIniFiles Memory Leaks
 
Zitat:

Zitat von himitsu
Wieso sollte man doppelte Sektionen in einer INI haben wollen?

Und wegen der ASM-Codes:
- man könnte diese für Unicode umbauen (1-Byte auf 2-Byte), aber dann laufen sie nicht mehr mit Ansi
oder man hat alles Doppelt und direkt die einzelnen Befehle entsprechend über Compilerschalter umschalten wäre noch unübersichtlicher, da es zuviele Stellen sind
- oder ... nja, ich hab einfach die ASM-Codes entfernt
> z.B. BinaryToHex durch Delphis BinToHex ersetzt und die restlichen Codes durch 'ne Pascal-Version
(ich hoffe mal diese Versionen laufen auch)

Ergebnis siehe anderer Thread (im DF)

Doppelte Sektionen sind z.B. bei Symantec in einer UPI-Datei. Solch eine UPI ist eine Ini-Datei für eine Deployment-Software. Also solange solch große Herstelle eigene Süppchen kochen muss man auf solche Units zurückgreifen.
Wenn deine Version laufen sollten, kannst du sie ja hier posten :-) Ich mache dann eine Delphi 7 und 2009 Version :-)

Btw, welcher andere Thread genau (zu faul zum suchen...)?

himitsu 22. Nov 2009 18:40

Re: FastIniFiles Memory Leaks
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von TDS
Btw, welcher andere Thread genau (zu faul zum suchen...)?

1 bzw. 3 Posts zuvor (Post #22)
http://www.delphi-forum.de/viewtopic...=585351#585351

[edit]
Style-Guide-Anpassungen übernommen (siehe #29)

API 23. Nov 2009 06:54

Re: FastIniFiles Memory Leaks
 
Ausgezeichnet - die Leaks sind verschwunden! Danke himitsu

Noch eine Frage: Entspricht das {$IF SizeOf(Char) > 1} dem {$IFDEF UNICODE} ?

himitsu 23. Nov 2009 07:40

Re: FastIniFiles Memory Leaks
 
Im Prinzip ja (hab's geändet), nur das dieses typsicherer ist und man es leicht auf's zukünftige Unicode4 erweitern kann.

Scheib mal ein {$define unciode} in die Datei oder ein "unicode" in die Bedingungen (siehe Projektoptionen) ... schon schaltet {$ifdef unicode} auf Unicode, aber die Typen sind dennoch in Ansi (<D09).

API 23. Nov 2009 08:19

Re: FastIniFiles Memory Leaks
 
Liste der Anhänge anzeigen (Anzahl: 1)
Den Code habe ich noch etwas formatiert ("Style-Guide Konform")

u.a.:
true --> True
false --> False
break --> Break

Zudem noch die Versionsnummer in FastIniFiles.pas aktualisiert. Sonst wurde nichts geändert.

himitsu 23. Nov 2009 09:49

Re: FastIniFiles Memory Leaks
 
komisch, ich dachte die Version hätte ich geändert :gruebel:

OK, hab deine Änderung mit in meinen Anhängen mit einfließen lassen.
(da ich nicht alles umformatieren wollte, hatte ich versucht den "originalen" Style beizubehalten ... war nicht unbedingt einfach)

[edit]
In der ZIP wurde nun auch die Dokumentation.html um die isUnicode-Eigenschaft erweitert.

himitsu 19. Feb 2010 11:50

Re: FastIniFiles Memory Leaks
 
Liste der Anhänge anzeigen (Anzahl: 1)
Doppelte Section- und Value-Namen standardmäßig erlaubt, allerdings schlägt {TFIniFile}.isValid dennoch absichtlich an und steht danach auf False.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:49 Uhr.

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