Package indylaz mit DEFINE FREE_ON_FINAL

Rund um die LCL und andere Komponenten
Antworten
TBug
Beiträge: 151
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.0 FPC 3.2.2
CPU-Target: Windows 32/64bit

Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von TBug »

Hallo zusammen,

wer von Euch verwendet die Indy-Komponenten mit {DEFINE FREE_ON_FINAL} einstellung in den Dateien:
\core\IdCompilerDefines.inc
\system\IdCompilerDefines.inc
um die unschönen Memory-Leaks beim Beenden der Anwendung zu unterbinden.

Gibt es irgendwelche Euch bekannten "tatsächlichen" Probleme, welche mir beim Verwenden noch nicht aufgefallen sind, oder läuft dies bei Euch problemlos?

Gibt es eventuell noch eine elegantere Lösung um den Speicher ordnungsgemäß freizugeben?

Danke

sstvmaster
Beiträge: 530
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.4
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von sstvmaster »

TBug hat geschrieben:
Di 20. Sep 2022, 20:43
Gibt es eventuell noch eine elegantere Lösung um den Speicher ordnungsgemäß freizugeben?
Thaddy hat dazu hier etwas geschrieben: https://forum.lazarus.freepascal.org/in ... #msg270350

Aber Remy meint das dazu: https://forum.lazarus.freepascal.org/in ... #msg428027
TBug hat geschrieben:
Di 20. Sep 2022, 20:43
Gibt es irgendwelche Euch bekannten "tatsächlichen" Probleme, welche mir beim Verwenden noch nicht aufgefallen sind, oder läuft dies bei Euch problemlos?
Ich denke nicht das es Probleme gibt, da in Delphi diese Leaks auch existieren und mit Sicherheit schon viel Software mit Indy geschrieben wurden ist.
LG Maik

Windows 10,
- Lazarus 2.2.4 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.5 (fixes) + fpc 3.3.1 (main/trunk)

Benutzeravatar
h-elsner
Beiträge: 200
Registriert: Di 24. Jul 2012, 15:42
OS, Lazarus, FPC: LINUX Mint18.3, Win10, Lazarus 2.0.12, FPC3.2.0
CPU-Target: 64Bit
Wohnort: Illertissen
Kontaktdaten:

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von h-elsner »

Ich wusste bisher gar nicht, dass es bei Indy Memoryleaks gibt. Schlimm kann es aber nicht sein, denn mit einem recht schwach mit Speicher ausgestattetem Raspberry PI läuft bei mir jahrelang ein Programm, welches per cron job sechsmal in der Stude gestartet, sich Daten von der PV-Anlage holt, diese verarbeitet und dann geschlossen wird. Das geht jahrelang schon so ohne Probleme. Sehr schlimm kann es also nicht sein.

Gruß HE

Socke
Lazarusforum e. V.
Beiträge: 3065
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von Socke »

h-elsner hat geschrieben:
Mi 21. Sep 2022, 10:09
Sehr schlimm kann es also nicht sein.
Sei froh, dass dein Linux mittlerweile virtuellen Speicher beherrscht. Damit weiß das Betriebssytem selbst Bescheid, welcher Speicher von einem Programm allokiert wurde und kann es nach Ende desProgramms wieder neu zuweisen. :D
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von Warf »

h-elsner hat geschrieben:
Mi 21. Sep 2022, 10:09
Ich wusste bisher gar nicht, dass es bei Indy Memoryleaks gibt.
Das sind keine Memory leaks, das ist Speicher der über die ganze Laufzeit des Programmes allokiert ist, und eigentlich wenn nur beim programm ende deallokiert werden muss, aber aus irgend welchen (guten) Gründen (die Remy schonmal im Englischen Forum genannt hat) das nicht tut. Aber da dann das Programm eh gekillt wird, ist das ja auch herzlichst egal, denn dann räumt das OS den Speicher direkt auf. Es gibt nur diese Unschöne meldung in Heaptrc.

Unter Delphi ist die "lösung" das man einfach dem Delphi heaptrc equivalent sagen kann das es diesen speicher ignorieren soll, damit wird der da auch nicht aufgeräumt, aber die Meldung wird unterdrückt, da es sich nicht wirklich um ein Leak handelt

TBug
Beiträge: 151
Registriert: Mi 2. Sep 2015, 11:09
OS, Lazarus, FPC: Lazaurus 2.2.0 FPC 3.2.2
CPU-Target: Windows 32/64bit

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von TBug »

Hallo zusammen,

danke ersteinmal für alle Antworten und die geposteten Links.
sstvmaster hat geschrieben:
Di 20. Sep 2022, 23:24
TBug hat geschrieben:
Di 20. Sep 2022, 20:43
Gibt es irgendwelche Euch bekannten "tatsächlichen" Probleme, welche mir beim Verwenden noch nicht aufgefallen sind, oder läuft dies bei Euch problemlos?
Ich denke nicht das es Probleme gibt, da in Delphi diese Leaks auch existieren und mit Sicherheit schon viel Software mit Indy geschrieben wurden ist.
Die Frage war anderstherum gemeint. Also nicht, ob es Probleme gibt, wenn der Speicher nicht aufgeräumt wird, sondern ob es "tatsächliche" Probleme gibt, wenn ich den Speicher aufräumen lasse, in dem ich eben FREE_ON_FINAL definiere, was ja standardmäßig nicht der Fall ist.

Bislang konnte ich selbst keine Probleme feststellen, dass eben nochmals auf die Speicherbereiche, nach dem sie freigegeben wurden, durch andere Units zugegriffen werden würde. Daher meine Frage nach Eueren Erfahrungen.

Es ist zwar richtig, dass der Speicher nach Beendigung des Programmes sowieso freigebenen wird, aber ich bin nun mal ein Freund davon, dass das Programm eigentlich dafür selbst zuständig sein sollte.
_

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

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von Warf »

TBug hat geschrieben:
Mi 21. Sep 2022, 18:30
Es ist zwar richtig, dass der Speicher nach Beendigung des Programmes sowieso freigebenen wird, aber ich bin nun mal ein Freund davon, dass das Programm eigentlich dafür selbst zuständig sein sollte.
Soweit ich mich erinner ist das Problem das folgende, der Speicher wird in der Initialization sektion von einer Unit initialisiert, und in der Finalization Sektion gefreed (wenn FREE_ON_FINAL gesetzt ist). Das Problem ist nun das es möglich ist das jemand eine Referenz auf diesen Speicher hat (objekt referenz, pointer, etc.) kann nicht garantiert werden das das finalize aufgerufen wird bevor es verwendet wird.

Beispiel: Projekt mit 2 Units die eine Zirkuläre referenz haben:
Unit 1:

Code: Alles auswählen

unit Unit1;

interface
uses
  Unit2;

var
  i: Integer = 42;
implementation
finalization
  i := 0;
end.
Unit 2:

Code: Alles auswählen

unit Unit2;

interface
implementation
uses
  Unit1;
  
finalization
  WriteLn(i);
end.
Die goldene Frage, was wird ausgegeben, 42 oder 0? Sowohl dependet Unit1 auf Unit2 durch das uses, aber auch anders rum. Es gibt also keine klare Abbhängigkeitsrelation.
Lösung: Es kommt drauf an wie die Units im Hauptprogramm eingebunden sind:

Code: Alles auswählen

program Project1;
uses unit1, unit2;
begin end.
Gibt 0

Code: Alles auswählen

program Project1;
uses unit2, unit1;
begin end.
Gibt 42.

Das heist, in dieser Konfiguration kann nicht garantiert werden welches finalize zu erst aufgerufen wird, sondern das ist vom Nutzer abbhängig. Wenn das jetzt also ein allokierter speicher wäre und statt :=0 würde da ein Free stehen, hätten wir ein Use-after-free und es kracht (vermutlich segfault, eventuell schlimmeres).
Als bibliothek kannst du nicht garantieren in welcher reihenfolge der Nutzer sachen included (gibt auf stackoverflow und co genug fragen von C++ entwicklern die winsock2.h nach der windows.h included haben und sich dann wundern warum es mehrere hunderte compiler fehler gibt), und damit nicht alle 3 tage jemand ankommt und meint Indy ist kaputt, weil sie die uses reihenfolge falsch gemacht haben, ist es da manchmal besser einfach die Hände in die Luft zu werfen und einfach Use after Free damit zu fixen indem man niemals Free aufruft.
Ist, wenn man nicht vor hat einen Referenzzählungsmechanismus zu implementieren, vermutlich die Sinnigste Lösung

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1123
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von fliegermichl »

Warum dann nicht ein FreeAndNil verwenden und vor der Benutzung prüfen, ob der Pointer nil ist?

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

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von Warf »

Wie gesagt ich erinnere mich nur Wage daran wie das genau in Indy ist (und Indy ist auch so groß das man es nicht einfach so spontan findet wenn mans nicht schon kennt). Aber grundsätzlich hilft FreeAndNil nur wenn es das selbe pointer objekt ist. Wenn die Unit eine Kopie des Pointers vorhält (z.B. eifach ein Objekt/Klasseninstanz mit := zugewiesen wird), z.B. wenn Polymorphisch verschiedene Objekte von der Unit verwendet werden, und daher nicht klar ist aus welcher Unit es kommt. Dann bringt FreeAndNil auch nichts.

Wenn ich mich recht erinnere war das in Indy eigentlich relativ sicher, aber es gab in der Theorie die Möglichkeit das es knallen könnte, in einigen konstellationen (aber eher ungewöhnlich), es aber keine möglichkeit gibt das anders sicherer zu bauen, ohne dabei praktisch alles einmal umzuschreiben. Aber wie gesagt, das sind alles nur Fragmente von einer Diskussion im Englischen Forum vor mehreren Jahren an die ich mich nur noch so schwammig erinnere

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1123
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von fliegermichl »

Gut, dann könnte man eine Function GibMirDenPointer machen welche dann abgefragt wird. Wenn ich mir eine Kopie eines fremderzeugten Pointers merke und darauf zugreife, bin ich selber schuld.

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

Re: Package indylaz mit DEFINE FREE_ON_FINAL

Beitrag von Warf »

Nein, das hilft auch nicht, das Problem ist essentiell unentscheidbar
Beispiel:

Code: Alles auswählen

unit Unit1;

interface
uses
  Unit2;

var
  i: Integer = 42;
implementation
initialization
  randomize;
  if Random(2) = 1 then
    Unit2.p := @i
  else
    Unit2.p := @Unit2.i;
finalization
  i := 0;
end.

Code: Alles auswählen

unit Unit2;

interface
var
  p: PInteger;
  i: Integer = 32;
implementation
uses
  Unit1;
finalization
  WriteLn(p^);
  i := 0;
end.
Hier kannst du nicht im vorhinein wissen auf welches i p zeigen wird, ob auf das aus Unit1 oder Unit2, da es bei programmstart zufällig gewürfelt wird.
Natürlich sind echte Programme nicht unbedingt Zufällig, aber oftmals abbhängig von nutzereingaben. Z.B. wenn das statt einem Integer eine Logger Klasse wäre, entweder ein Datei Logger oder ein Konsolen Logger (wobei der Dateilogger natürlich die Datei am Ende schließen muss um zu garantieren das alles geschrieben wurde), und der Nutzer das mit --debug=console oder --debug=filename angeben könnte, ist das effektiv genauso unvorhersehbar welche instanz verwendet wird.

Sobald die verwendete Instanz dynamisch entschieden wird, ist das problem inherent unentscheidbar (also Mathematisch beweisbar das es unmöglich rauszufinden welches Objekt verwnedet wird) und es gibt nix was man tun kann.
Speicherverwaltung ist ein inherent unentscheidbares Problem, weshalb es auch so schwer ist diese zu Lösen. Ich hab das schon öfter angebracht, 2019 hatte Mozilla mal einen Review über alle bugs innerhalb der CSS komponente von Firefox gemacht, und rausgefunden das etwa 45% der Bugs ausschließlich mit Speichermanagement zu tun hatten (also genau sowas wie use-after free). Speicherverwaltung richtig zu machen ist einfach unglaublich schwer, und FreeAndNil hilft nur in ganz trivialen Fällen.

Meine Faustregel ist sogar, wenn du FreeAndNil brauchst, dann ist das Programm schlecht designed. In einem gut Designten Code sollte shared state soweit wie möglich vermieden werden, und wenn nötig, dann entweder mit sicherer Refernzzählung, oder einem Fixen ownership prinzip, das es nie vorkommen kann das zwei codestellen auf die selbe variable zugreifen und nicht klar ist wer länger lebt.

Versteh mich nicht Falsch, ich bin auch der Meinung das das in Indy eigentlich nicht passieren sollte, aber ich weiß wie große projekte sich verhalten, und wenn man im nachhinein erst feststellt das in manchen konfigurationen es knallen kann, ist es meist schon zu spät um sein gesammtes speicherverwaltungssystem von der komponente einmal umzudesignen. Da ist es manchmal besser einfach einen hack wie den zu machen, als einmal das gesammte projekt umschreiebn zu müssen

Antworten