Speicher aufräumen

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
Benutzeravatar
Winni
Beiträge: 980
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.0.12, fpc 3.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Speicher aufräumen

Beitrag von Winni »

Hi!

Das wäre eine gute Idee, wenn nicht ...

Einfach ist es derart:

Code: Alles auswählen

MyVar := TSomeObject.create(...
...
MyVar.Free
Aber es gibt auch vertracktere Situationen.
Ein einfaches Beispiel:

Code: Alles auswählen

Var MyFiles : TStringList;
...
MyFiles := FindAllFiles(....

Derlei Funktionen gibt es viele und das würde ja wohl auf ein Expertensystem hinauslaufen, wenn man die alle erfassen wollte, die irgendeine Art von free (oder destroy) benötigen.


Winni

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 4755
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Speicher aufräumen

Beitrag von af0815 »

Das ist das was ich damit gemeint habe, der erste Fall ist einfach, der zweite von der Automatik nicht wirklich behandelbar.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

PascalDragon
Beiträge: 394
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Speicher aufräumen

Beitrag von PascalDragon »

Epcop hat geschrieben:
Fr 5. Mär 2021, 07:32
Gibt es in der Entwicklungsumgebung ein Tool, dass auf fehlende ".free" hinweißt? (o.ä.)
Damit könnte man doch einfacher solche lecks finden oder?!
Das ist doch letztlich das was heaptrc macht: wenn ein Speicherblock nicht freigegeben wurde (als zum Beispiel ein Free fehlt), dann wird das am Ende angezeigt zusammen mit der Stelle wo der Speicherblock erzeugt wurde.
FPC Compiler Entwickler

Warf
Beiträge: 1585
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Speicher aufräumen

Beitrag von Warf »

Ich kann auch valgrind wärmstens empfehlen. Heaptrc ist zwar schön und gut, aber ich hatte schon sehr oft das ich keine genauen (oder gar keine) code locations bekommen habe, mit denen man recht wenig anfangen kann. In manchen Fällen hat man mit valgrind manchmal mehr Glück. Außerdem kann valgrind noch mehr als die Heaptrc unit, z.b. use after frees oder illegal accesses detektieren.
Allerdings sollte man warnen, die ausführung innerhalb valgrinds ist bestimmt ein faktor 100-1000 langsamer als nativ, dennoch ist es das meist wert, vor allem bei nicht trivialen Projekten

Benutzeravatar
photor
Beiträge: 261
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux (L 2.0.12 FPC 3.2.2)
CPU-Target: 64Bit

Re: Speicher aufräumen

Beitrag von photor »

PascalDragon hat geschrieben:
Fr 5. Mär 2021, 15:18
Das ist doch letztlich das was heaptrc macht: wenn ein Speicherblock nicht freigegeben wurde (als zum Beispiel ein Free fehlt), dann wird das am Ende angezeigt zusammen mit der Stelle wo der Speicherblock erzeugt wurde.
genau das mache ich gerade mit meinem Code. Aber Heaptrc gibt mir bei mehreren Leaks die selbe Stelle im Code an. Werden jetzt mehrere Speicherblöcke nicht mehr freigegeben :?: (es handelt sich teilweise um die MainForm.Create-Prozedur, die ja nun nur einmal durchlaufen wird. :?

Und es ist nicht immer ganz einfach die Stelle zu finden, an der das .Free dann angebracht ist. Das in die MainForm.MenuButtonQuitClick-Prozedur zu packen, hilft jedenfalls nicht immer.

Na, ich hab' ne Aufgabe für die nächsten Tage.

Ciao,
Photor

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 4755
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Speicher aufräumen

Beitrag von af0815 »

Heaptrace ist nicht einfach. man bekommt im laufe der Zeit aber einen Blick. Ich würde mir einmal die ersten logischen Stellen ansehen. Klassiker sind Exceptions die später gefangen wurden, aber vorher noch ein paar Leichen stehen lassen. Ist eine Übungssache, wennich einen Verdacht habe, rufe ich ein Passage im Programm gezielter auf und schau dann im heaptrace die Löcher sich vermehren.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
Winni
Beiträge: 980
Registriert: Mo 2. Mär 2009, 16:45
OS, Lazarus, FPC: Laz2.0.12, fpc 3.2
CPU-Target: 64Bit
Wohnort: Fast Dänemark

Re: Speicher aufräumen

Beitrag von Winni »

photor hat geschrieben:
Fr 5. Mär 2021, 20:12
Aber Heaptrc gibt mir bei mehreren Leaks die selbe Stelle im Code an. Werden jetzt mehrere Speicherblöcke nicht mehr freigegeben :?: (es handelt sich teilweise um die MainForm.Create-Prozedur, die ja nun nur einmal durchlaufen wird. :?

Ciao,
Photor
Hi!

Ja, das ist der Ärger, den ich schon oben erklärt habe.
Also nochmal simple:

Procedure A hat das Speicherleck. Wenn es das Ende der Verweis-Kette ist, muss man hier suchen.

Aber:
Procedure C ruft Procedure B ruft Procedure A:

Alle 3 Proceduren werden angezeigt, weil C und B ja das Speicherleck von A "erben".

Also immer im Quellcode sich von Zeile zu Zeile hangeln, bis von der Zeile kein Verweis mehr auf andere Zeilen folgt. Innerhalb der Procedure muss irgendwo das Malheur passiert sein.

Wenn man mein obiges Beispiel nimmt arbeitet man topdown:

$00000000004646A6 LOADKDEDECK, line 325 of skat1.pas
$00000000004674AD FORMCREATE, line 974 of skat1.pas
$000000000044EE47 DOCREATE, line 939 of include/customform.inc
$000000000044CABD AFTERCONSTRUCTION, line 149 of include/customform.inc
$0000000000455634 CREATE, line 3184 of include/customform.inc
$000000000045F955 CREATEFORM, line 2239 of include/application.inc
$000000000041F05E main, line 19 of skat.lpr

main in der lpr ruft CreateForm, das ruft Create, taucht ab in die internen Functionen der Form nämlich AfterConstrution und DoCreate. Nun bist Du wieder beim "Heimspiel" - nämlich FormCreate. Da rufst Du LoadKdeDeck - und von hier gibt's keine weiteren Verweise. Als muss hier irgendwo der Fehler stecken. Dass er nun keinen weiteren Verweis auf die bewusst fehlerhaft gemachte BGRArotate90 macht, ist mir auch nicht klar. Aber Du bist schon mal in der richtigen Region:

Du musst also alles, was in diesem Fall in LoadKdeDeck gerufen wird, überprüfen. Irgendwo hier muss es sein.

Um sich nicht völlig zu verirren: In Zukunft zwischendurch immer mal heaptrc checken lassen, ob man in der letzten Stunde irgenwo nen Bolzen eingebaut hat. Wenn man am Ende des Projects auf die Jagd nach 23 Fehlern gehen muss, wird es echt unübersichtlich.

Keep on hackin'
Winni

Benutzeravatar
photor
Beiträge: 261
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux (L 2.0.12 FPC 3.2.2)
CPU-Target: 64Bit

Re: Speicher aufräumen

Beitrag von photor »

Winni hat geschrieben:
Fr 5. Mär 2021, 23:30
Ja, das ist der Ärger, den ich schon oben erklärt habe.
Also nochmal simple:

Procedure A hat das Speicherleck. Wenn es das Ende der Verweis-Kette ist, muss man hier suchen.

Aber:
Procedure C ruft Procedure B ruft Procedure A:

Alle 3 Proceduren werden angezeigt, weil C und B ja das Speicherleck von A "erben".

Also immer im Quellcode sich von Zeile zu Zeile hangeln, bis von der Zeile kein Verweis mehr auf andere Zeilen folgt. Innerhalb der Procedure muss irgendwo das Malheur passiert sein.

Wenn man mein obiges Beispiel nimmt arbeitet man topdown:

$00000000004646A6 LOADKDEDECK, line 325 of skat1.pas
$00000000004674AD FORMCREATE, line 974 of skat1.pas
$000000000044EE47 DOCREATE, line 939 of include/customform.inc
$000000000044CABD AFTERCONSTRUCTION, line 149 of include/customform.inc
$0000000000455634 CREATE, line 3184 of include/customform.inc
$000000000045F955 CREATEFORM, line 2239 of include/application.inc
$000000000041F05E main, line 19 of skat.lpr

main in der lpr ruft CreateForm, das ruft Create, taucht ab in die internen Functionen der Form nämlich AfterConstrution und DoCreate. Nun bist Du wieder beim "Heimspiel" - nämlich FormCreate. Da rufst Du LoadKdeDeck - und von hier gibt's keine weiteren Verweise. Als muss hier irgendwo der Fehler stecken. Dass er nun keinen weiteren Verweis auf die bewusst fehlerhaft gemachte BGRArotate90 macht, ist mir auch nicht klar. Aber Du bist schon mal in der richtigen Region:

Du musst also alles, was in diesem Fall in LoadKdeDeck gerufen wird, überprüfen. Irgendwo hier muss es sein.

Um sich nicht völlig zu verirren: In Zukunft zwischendurch immer mal heaptrc checken lassen, ob man in der letzten Stunde irgenwo nen Bolzen eingebaut hat. Wenn man am Ende des Projects auf die Jagd nach 23 Fehlern gehen muss, wird es echt unübersichtlich.
Genau das ist der Lernauftrag an mich! Muss mich da jetzt durch hangeln, eventuell was ändern und sehen, ob's geholfen hat. Aufgeben werde ich so schnell nicht.

Ciao,
Photor

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 4755
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Niederösterreich
Kontaktdaten:

Re: Speicher aufräumen

Beitrag von af0815 »

Fang statisch an :D Programm starten und wieder beenden. :shock:
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Benutzeravatar
photor
Beiträge: 261
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux (L 2.0.12 FPC 3.2.2)
CPU-Target: 64Bit

Re: Speicher aufräumen

Beitrag von photor »

Zum Speicher aufräumen noch eine Frage: in wie weit muss man TChart-Strukturen (LineSeries, Charts etc.) freigeben?

Ciao,
Photor

wp_xyz
Beiträge: 4016
Registriert: Fr 8. Apr 2011, 09:01

Re: Speicher aufräumen

Beitrag von wp_xyz »

Normalerweise gar nicht. Das sind Komponenten mit einem Owner, der fürs Aufräumen zuständig ist. Also wenn du einen Chart aufs Formular klickst, wird das Formular der Owner, und es gibt den Chart frei, wenn es selbst freigegeben wird. Genauso mit den Series etc.

Nur wenn du einen Chart etc zur Laufzeit selbst erzeugst und im Create den Owner mit nil angibst, musst du dich selbst um das Aufräumen kümmern. Da ist TAChart nicht anders als alle anderen Komponenten.

Benutzeravatar
photor
Beiträge: 261
Registriert: Mo 24. Jan 2011, 21:38
OS, Lazarus, FPC: Arch Linux (L 2.0.12 FPC 3.2.2)
CPU-Target: 64Bit

Re: Speicher aufräumen

Beitrag von photor »

Ok. Danke. Dann brauche ich da nicht zu suchen.

Ciao,
Photor

wp_xyz
Beiträge: 4016
Registriert: Fr 8. Apr 2011, 09:01

Re: Speicher aufräumen

Beitrag von wp_xyz »

Die üblichen Kandidaten für Speicherlecks sind bei mir
  • TList, TFPList: vergessen die Listenelemente zu zerstören
  • TStringList, wenn eigens erzeugte Objekte eingehängt werden (AddObject). Auch diese müssen explizit zerstört werden.
  • Bei diesen Listen Clear aufrufen ohne die Listenelemente/eingehängten Objekte freizugeben
  • Verschachtelte Klassenstruktur (Klasse in Klasse in Klasse in ...), und eine der inneren Klassen gibt im Destructor die untergeordneten Klassen nicht frei.
  • rabiater Umgang mit Strings (FillChar)
  • Routinen mit harmlosem Namen: FindAllFiles gibt es als Prozedur mit der die Dateinamen aufnehmenden StringList als Parameter - das ist ok, aber auch als Funktion mit der StringList als Ergebnis - das ist gefährlich:

    Code: Alles auswählen

    Listbox1.Lines := FindAllFiles(...)  // ---> Speicherleck weil die Result-StringList von FindAllFiles nicht freigegeben wird 

Warf
Beiträge: 1585
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Speicher aufräumen

Beitrag von Warf »

Auch böse sind Exceptions:

Code: Alles auswählen

function foo: TStringList;
begin
  Result := TStringList.Create;
  DoSomething; // Kann eine exception auslösen
end;

procedure Bar;
var
  sl: TStringList;
begin
  try
    sl := foo;
    try
      Memo1.Lines := sl;
    finally
      sl.free;
    end;
  except on E: Exception do
    ShowMessage(e.Message);
  end;
end;
Hier kann bei einer exception das Result nicht gefreed werden. Da muss dann sowas hin:

Code: Alles auswählen

function foo: TStringList;
begin
  Result := TStringList.Create;
  try
    DoSomething; // Kann eine exception auslösen
  except on E: Exception
    Result.Free;
    raise E;
  end;
end;
Etwas nervig immer dran denken zu müssen, grade wenn man viel mit exceptions arbeitet.

PascalDragon
Beiträge: 394
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Speicher aufräumen

Beitrag von PascalDragon »

wp_xyz hat geschrieben:
Mo 8. Mär 2021, 19:39
  • TStringList, wenn eigens erzeugte Objekte eingehängt werden (AddObject). Auch diese müssen explizit zerstört werden.
Nur so als Hinweis: TStringList unterstützt seit FPC 2.4.4 die OwnsObjects Eigenschaft, welche dafür sorgt, dass Objekte freigegeben werden (also auch beim Entfernen von Einträgen zum Beispiel). ;)
FPC Compiler Entwickler

Antworten