Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Speicherleck bei der Verwendung von anonymen Methoden (https://www.delphipraxis.net/163779-speicherleck-bei-der-verwendung-von-anonymen-methoden.html)

carlo93 15. Okt 2011 22:40

Delphi-Version: XE

Speicherleck bei der Verwendung von anonymen Methoden
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo!

Folgendes Problem:

Bei der Verwendung dieses Quelltextes entsteht ein Speicherleck:
Delphi-Quellcode:
type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    FProc: TProc;
  public
    { Public-Deklarationen }
  end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Func: TFunc<String>;
begin
  ReportMemoryLeaksOnShutdown:=true;
  Func:=
    function: String
    begin
      Result:=Edit1.Text;
    end;
  Memo1.Lines.Add(Func);
  FProc:=
    procedure
    begin
      Memo1.Lines.Add(Func);
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FProc;
end;
Anhang 35373

Momentan kann ich leider nicht überprüfen, ob das auch bei XE2 auftritt (meine Testversion ist gerade abgelaufen).
Gibt es eine Möglichkeit, dieses Speicherleck zu verhindern?

jaenicke 16. Okt 2011 09:38

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
Umgehen lässt sich das Problem leicht:
Mach aus Func auch ein privates Feld statt einer lokalen Variable.

Schöner ist aber wohl diese Lösung:
Delphi-Quellcode:
procedure TForm142.FormCreate(Sender: TObject);

  procedure DoInit(Func: TFunc<String>);
  begin
    Memo1.Lines.Add(Func);
    FProc :=
      procedure
      begin
        Memo1.Lines.Add(Func);
      end;
  end;

begin
  ReportMemoryLeaksOnShutdown := true;
  DoInit(function: String
    begin
      Result := Edit1.Text;
    end);
end;

himitsu 16. Okt 2011 09:49

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
Da wird wohl die eingebetette Reference nicht mehr freigegeben.

Am Generic liegt es nicht, dann wenn man
Delphi-Quellcode:
TFunc<String>
durch einen richtigen Typen ersetzt, ändert sich nichts.


Lösungen:
- im QC melden und hoffen es wird eventuell irgendwann in den nächsten Jahrzehnten behoben
- auf verschachtelte Referencen verzichten
- oder Func ebenfalls als privates FFunc in der Form speichern [add] wie schonmal genannt
- diesen Fehler einfach ignorieren (wird ja eh nie behoben)
- schmutzige Tricks, um diesen Fehler provisiorisch zu umgehn *1




1)
Delphi-Quellcode:
IInterface(PPointer(@Func)^)._Release;
als letzen Befehl in FormCreate,
aber sollte dieser Fehler wirklich mal irgendwann behoben werden, dann raucht dir die Anwendung ab. :stupid:

jaenicke 16. Okt 2011 11:08

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
Gerade getestet: Das Problem tritt auch bei XE2 noch auf.

Bernhard Geyer 16. Okt 2011 11:19

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
Tritt das Speicherleck bei jeder verwendung auf oder einmalig beim ersten Aufruf?

jaenicke 16. Okt 2011 11:21

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
Einmal, es wird ja schließlich die Funktion nicht freigegeben und die existiert nun einmal nur einmal...

// EDIT:
Bzw. wenn man den Code aus FormCreate mehrfach ausführt, wird natürlich bei jeder Ausführung ein Speicherleck produziert.

Stevie 16. Okt 2011 15:13

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
Ist schon lange bekannt: QC #83259 und QC #78066.

Tip: Wenn ihr beschleunigen wollt, dass bestimmte Einträge gefixt werden, dann nutzt die Vote Funktion (man kann entgegen dem Irrglauben einiger pro Eintrag 10 Punkte vergeben).

carlo93 16. Okt 2011 15:29

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
@jaenicke

Vielen Dank für's Testen und die nette Antwort!

Zitat:

Zitat von Stevie (Beitrag 1130716)
Ist schon lange bekannt

@Stevie

Ich hatte nicht gefragt, ob das Problem bekannt ist, sondern
  1. ob das Problem unter XE2 auch noch auftritt und
  2. ob es dafür (schon) eine Lösung gibt.

Ohne einen überheblichen Ton kannst du wohl überhaupt nichts von dir geben? :wall:

Stevie 16. Okt 2011 15:48

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
Zitat:

Zitat von carlo93 (Beitrag 1130719)
Ohne einen überheblichen Ton kannst du wohl überhaupt nichts von dir geben? :wall:

Ich habe lediglich darauf hingewiesen, dass das Problem bekannt und nicht gefixt ist.
Da der Eintrag nicht geclosed ist, tritt der Bug noch in XE2 auf (1.)
Wenn du dir die Einträge anschaust, siehst du woran das Problem liegt und auch Workarounds (2.)

Im übrigen wurde hier in diesem Thread darauf hingewiesen, einen QC Eintrag zu machen - daher habe ich mir die Mühe gemacht, das mal nachzuschauen.

Wenn du Sachlichkeit mit Überheblichkeit verwechselst, tut's mir leid für dich.

Thom 16. Okt 2011 17:10

AW: Speicherleck bei der Verwendung von anonymen Methoden
 
@carlo93

Zitat:

Zitat von carlo93 (Beitrag 1130719)
Ohne einen überheblichen Ton kannst du wohl überhaupt nichts von dir geben?

Nö - das ist seine "nette" Art mit anderen umzugehen... Das findest Du in fast jedem seiner Beiträge. :zwinker:

Zu Deiner Frage:
Der Compiler speichert die von anonymen Methoden genutzen Variablen (Variablenbindung). Diese werden in TInterfacedObject-Derivaten mit Referenzzählung abgelegt.
Aus für mich nicht ersichtlichen Gründen besitzen diese Frames nach der Erstellung der anonymen Methode einen RefCount-Wert von 2. Nach Beendigung der Methode (in Deinem Fall TForm1.Create) wird dieser Wert wieder um zwei verringert und das Frame-Objekt aus dem Speicher entfernt. Tritt jetzt der Fall ein, das die anonyme Methode in einer anderen weiterverwendet wird, erhöht der Compiler RefCount des Frame-Objektes um eins und es wird beim Aufräumen am Ende der Methode nicht mehr gelöscht. So weit - so gut. Dummerweise verringert der Compiler in diesem Fall RefCount nur um eins und somit bleibt das Frame-Objekt auch zum Schluß erhalten. Meiner Meinung nach ist das ein Fehler im Compiler.
Da ich bei der Programmierung meines Frameworks auch auf dieses Problem gestoßen bin, habe ich dafür eine einfache Lösung entwickelt:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var
  Func: TFunc<String>;
begin
  ReportMemoryLeaksOnShutdown:=true;
  Func:=
    function: String
    begin
      Result:=Edit1.Text;
    end;
  Memo1.Lines.Add(Func);
  FProc:=
    procedure
    begin
      Memo1.Lines.Add(Func);
    end;
  {$IF (CompilerVersion>=20) and (CompilerVersion<24)} //<- optimistisch ;-)
  if RefCount(Func)>2 //-> es existieren zusätzliche Referenzen
    then ReleaseMethod(Func); //<- RefCount wird um das fehlende Mal verringert
  {$IFEND}
end;
Zitat:

Zitat von himitsu (Beitrag 1130681)
Lösungen:
- [...]
- [...]
- [...]
- [...]
- schmutzige Tricks, um diesen Fehler provisiorisch zu umgehn *1

1)
Delphi-Quellcode:
IInterface(PPointer(@Func)^)._Release;
als letzen Befehl in FormCreate,
aber sollte dieser Fehler wirklich mal irgendwann behoben werden, dann raucht dir die Anwendung ab. :stupid:

Insofern entspricht das etwa himitsu's Lösung Nummer 5 mit vorherigem Test. :thumb:
Delphi-Quellcode:
  {$IF (CompilerVersion>=20) and (CompilerVersion<???)} 
  if IInterface(PPointer(@Func)^)._AddRef>3
    then IInterface(PPointer(@Func)^)._Release;
  IInterface(PPointer(@Func)^)._Release;
  {$IFEND}


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:31 Uhr.
Seite 1 von 3  1 23      

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