Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Speicherleck bei Verwendung von TInterfacedPersistent? (https://www.delphipraxis.net/101853-speicherleck-bei-verwendung-von-tinterfacedpersistent.html)

squetk 19. Okt 2007 01:52


Speicherleck bei Verwendung von TInterfacedPersistent?
 
Hallo,

leite ich Klassen von TInterfacedPersistent ab, meldet mir FastMM, dass diese Klasse ein Speicherleck verursacht.
Folgendes Beispiel:
Delphi-Quellcode:
unit ClassesSB;

interface

uses SysUtils, Classes;

type
  ISBParam = interface(IInterface)
    ['{3A2C91E1-9935-436B-BCCE-489F0615F5D1}']
    function GetValue(const sParamFieldName : string; const iParamType : integer) : string;
  end;

  TSBParamClass = class of TSBParam;

  TSBParam = class(TInterfacedPersistent, ISBParam)
  //TSBParam = class(TInterfacedObject, ISBParam)
  private
  public
    function GetValue(const sParamFieldName : string; const iParamType : integer) : string; virtual;
  end;

  TSBParamTest = class(TSBParam)
  private
  public
    function GetValue(const sParamFieldName : string; const iParamType : integer) : string; override;
  end;

implementation

{ TSBParam }

function TSBParam.GetValue(const sParamFieldName: string;
  const iParamType: integer): string;
begin
  Result := '';
end;

{ TSBParamTest }

function TSBParamTest.GetValue(const sParamFieldName: string;
  const iParamType: integer): string;
begin
  Result := Format('Param=%s Type=%d', [sParamFieldName, iParamType]);
end;

initialization

  RegisterClasses([TSBParamTest]);


end.
Und so wird es verwendet:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var sbParam : ISBParam;
begin
  sbParam := TSBParamClass(FindClass('TSBParamTest')).Create;
  //sbParam := TSBParamTest.Create;
  ShowMessage(sbParam.GetValue('FieldName', 1));
end;
Nutze ich TInterfacedObject als Basisklasse (Kommentare), ist FastMM zufrieden. Allerdings hatte ich vor, die Klassen aus dem Namen als Text zu erzeugen (die Klassennamen sind extern gespeichert), und FindClass arbeitet nur mit Derivaten von TPersistentClass.

Sicher wäre es ein Wordaround, das FindClass mit einer eigenen Listenklasse nachzubilden - aber den Aufwand würde ich mir gern sparen. Ausserdem interessiert es mich, ob wirklich ein Speicherleck entsteht oder FastMM hier eine Falschmeldung produziert.

Dax 19. Okt 2007 02:33

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Ich kann es nicht beweisen, aber ich vermute mal ins blaue, dass TInterfacedPersistent die Referenzzählung aushebelt. Produziert folgender Code ein Leck?
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var sbParam : ISBParam; temp: TSBParamClass;
begin
  temp := TSBParamClass(FindClass('TSBParamTest')).Create;
  sbParam := temp;
  //sbParam := TSBParamTest.Create;
  ShowMessage(sbParam.GetValue('FieldName', 1));
  temp.Free;
end;

squetk 19. Okt 2007 02:39

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Es gibt beim temp.Free eine Schutzverletzung - ist das eventuell ein Indiz dafür, dass FastMM eine Speicherleck-Falschmeldung produziert?

Dax 19. Okt 2007 02:43

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Args! Ich sollte nicht so spät noch Fragen beantworten... Das sollte natürlich TSBParam in der Variablendeklaration heißen, nicht TSBParamClass *schämt sich in grund und boden*

squetk 19. Okt 2007 02:49

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Da hat mich schon der Compiler drauf aufmerksam gemacht, das war also nicht die Ursache für die Schutzverletzung, die Variablendeklaration hatte ich berichtigt.
Zitat:

Ich sollte nicht so spät noch Fragen beantworten...
Spät? Ich finds eher früh :wink:

Dax 19. Okt 2007 02:53

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Zitat:

Zitat von squetk
Spät? Ich finds eher früh :wink:

Sag das mir, so ich seit Wochen nich mehr richtig und seit gestern garnich mehr schlafen kann.. Aber zum Thema: wenn es selbst bei einer richtigen Deklaration der Variablen eine Schutzverletzung gibt, würde ich diese Konstellation zum einen mit einem anderen Memorymanager ausprobieren (sollte das selbe Ergebnis liefern) und in einem anderen Test die Interfaces weglassen. Wie dein Leck zustande kommt, entzieht sich meiner Erkenntnis.. Und die AV deutet auf einen schweren Fehler in einem anderen Programmteil hin. Hast du eventuell Destroy überschrieben und inherited nicht drin? Oder noch schlimmer, Free redeklariert?

squetk 19. Okt 2007 03:01

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Nein - solch schlimme Sachen habe ich nicht gemacht. Alles was ich gecodet habe, ist im ersten Beitrag zu sehen - ganz harmlos.
Sobald ich statt TInterfacedPersistent TInterfacedObject verwende, habe ich ja das gewünschte Ergebnis. Dann kann ich aber FindClass nicht nutzen.
Ich habe mal in den Sourcen nachgeschaut (Classes.pas), dort werden die Methoden _AddRef etc. bei TInterfacedPersistent anders implementiert als bei TInterfacedObject. Merkwürdig.

Dax 19. Okt 2007 03:03

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Also wie vermutet, die Referenzzählung wird ausgehebelt.. Was in dem Fall das beste Vorgehen ist kann ich nicht sagen, aber wenn du auf die Referenzzählung angewiesen bist, könntest du versuchsweise die Implementation aus TInterfacedObject in deine Basisklasse packen (und eventuell dort behalten).

squetk 19. Okt 2007 03:07

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Also eine Klasse von TInterfacedPersistent ableiten und dann das Referenz-Handling von TInterfacedPersistent mit der Implementation von TInterfacedObject überschreiben?
Klingt nicht übel.
Mal schaun ob das klappt.

squetk 19. Okt 2007 05:56

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Komisch: Ich habe eine Klasse TPatchInterfacedPersistent von TInterfacedPersistent abgeleitet und die _AddRef und _Release-Methoden und alle weiteren mit der Implementation von TInterfacedObject überschrieben und die das Interface implementierenden Klassen davon abgeleitet - trotzdem kommt es zum Speicherleck.

Da scheint ein anderes Problem in TInterfacedPersistent verborgen zu sein. :gruebel:

Ich werde wohl das Handtuch schmeissen und eine eigene Listenklasse coden, die mir die FindClass-Funktionalität liefert. Dann können meine implementierenden Klassen von TInterfacedObject abgeleitet sein und es herrscht eitel Sonnenschein.

Flocke 19. Okt 2007 07:24

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Die Vermutung von Dax war richtig: TInterfacedPersistent hebelt die Referenzzählung aus.

TInterfacedObject ist direkt für die Verwendung des Interfaces gedacht, also
Delphi-Quellcode:
Variable := TMeineKlasse.Create(...) as IMeinInterface;
Danach sollte Delphi sich um alles weitere kümmern, insbesondere sollte man mit der Basisklasse nicht .Free aufrufen - das geht alles über die Referenzzählung.

Bei TInterfacedPersisted ist es genau umgekehrt: es erfolgt keine Referenzzählung. Man muss ein solches Objekt also explizit mit .Free der Basisklasse freigeben. Wichtig ist hierbei aber: Delphi kennt die Implementierung der Basisklasse nicht! Daher produziert das Beispiel von Dax einen Fehler, wenn Delphi nach dem .Free implizit versucht, die Methode ._Release des Interfaces aufzurufen (dessen Basisobjekt ja schon freigegeben ist). Ein einfaches "SBParam := nil;" vor dem "temp.Free;" sollte den Fehler beheben.

Eigentlich sollte der Vorschlag mit der Ersetzung der Referenzzählung funktionieren. Die implizite Verwaltung der Referenzzählung durch Delphi ist allerdings eine vertrackte Sache, bei der ich auch schon oft auf die Nase geflogen bin.

jbg 19. Okt 2007 11:40

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Zitat:

Zitat von Flocke
Die implizite Verwaltung der Referenzzählung durch Delphi ist allerdings eine vertrackte Sache, bei der ich auch schon oft auf die Nase geflogen bin.

Das ist eigentlich ganz einfach:
Alles was von TInterfacedObject abgeleitet ist, nutzt die Referenzzählung.
Alles was von TInterfacedPersistent bzw. TComponent abgeleitet ist nutzt keine Referenzzählung.
Es gibt natürlich noch die Ausnahmen, bei denen die Referenzzählung entweder nachträglich deaktiviert oder eingebaut wurde. Aber hierbei sollte der Programmierer schlau genug sein um seinen eigenen Code zu kennen.

squetk 19. Okt 2007 14:07

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Stimmt! :bounce2:
Wenn ich das Interface auf nil setze und dann die Basisklasse freigebe, funktioniert es anstandslos.
Dieser Umstand ist leider der Delphi-Hilfe nicht zu entnehmen.
Danke @all

Flocke 19. Okt 2007 14:37

Re: Speicherleck bei Verwendung von TInterfacedPersistent?
 
Zitat:

Zitat von jbg
Das ist eigentlich ganz einfach:

Inzwischen weiß ich das auch ;)
Wenn man es einmal durchschaut hat, ist es auch ganz einfach. Aber: es ist nirgendwo dokumentiert, im Quelltext sind keine Kommentare und von den Namen her kann man es auch nicht ableiten.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:12 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