[geklärt] Fragen zur Nutzung von Methode free

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

[geklärt] Fragen zur Nutzung von Methode free

Beitrag von Erwin »

Hallo. Hoffe es gehört in diesen Bereich.

Immer wieder lese ich in allen möglichen Zusammenhang den Befehl 'free'. Ich weiß zwar im Groben was dieser macht. Nämlich Speicher frei geben, wenn ich das richtig verstanden habe. Leider wird es weder in meinem FreePascal-Buch behandelt, noch ist es mir in anderen Bücher über Delphi darüber groß was aufgefallen. Des weiteren verwundert es mich, dass so ein Befehl überhaupt genutzt wird? Damals unter Delphi (bzw. in den Bücher dazu) hieß es, dass man sich über den Speicher und dessen Freigabe keine Gedanken machen muss, weil dafür Delphi sorgt. Und ebenso auch beim Beenden all der Speicher wieder freigegeben wird. Da Lazarus und Delphi viel gemeinsam haben, ging ich davon aus, dass bei Lazarus sich ebenso verhält.

Also entweder stimmt dies nicht, oder es gibt da Besonderheiten, über die mich bis jetzt kein Buch aufgeklärt hat. Weder Bücher über Delphi und Freepascal/Lazarus.

Würde mich freuen, wenn mir das mal einer erklären könnte, was es damit eigentlich auf sich hat? Ob man den Befehl free nutzen muss? Oder Geschmackssache ist. Oder in Ausnahmefällen aus Sicherheitsgründen genutzt wird. Oder gibt es noch andere Gründe dafür?
Zuletzt geändert von Erwin am Mo 20. Nov 2017, 21:58, insgesamt 2-mal geändert.
Lazarus 2.2.0 / FP 3.2.4

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fragen zur Nutzung von Befehl free

Beitrag von Mathias »

Damals unter Delphi (bzw. in den Bücher dazu) hieß es, dass man sich über den Speicher und dessen Freigabe keine Gedanken machen muss, weil dafür Delphi sorgt.
Dies hat wohl ein Java-Programmierer geschrieben.
Einzig was stimmt, bei LCL-Komponenten, dort wird bei Close, alles automatisch frei gegeben, vorausgesetzt, die Komponente wurde mit Self erzeugt.

Code: Alles auswählen

var
  Button: TButton;
begin
  Button := TButton.Create(self);

Aber alle anderen Klassen brauchen dringend ein Free.
So nebenbei Ruft ein Free den Destructor auf.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Fragen zur Nutzung von Befehl free

Beitrag von Warf »

Zunächst einmal etwas Begriffserklärung, man nennt das nicht Befehl. Befehl (zu englisch Instruction) bezeichnet für gewöhnlich einzelne Assembler Kommandos. Dann gibt es Funktionen/Prozeduren, Funktionen bezeichnet eine Funktion (z.B. IntToStr) die einen Rückgabewert haben. Prozeduren sind das selbe ohne Rückgabewert. Wenn man Objekte Verwendet heißen die Funktionen von Objekten Methoden. Free ist also eine Methode. Dieser Unterschied mag zwar relativ irrelevant für dich sein (zumindest aktuell), aber für die Zukunft weist dus jetzt.

Zu deiner Frage:
Damals unter Delphi (bzw. in den Bücher dazu) hieß es, dass man sich über den Speicher und dessen Freigabe keine Gedanken machen muss, weil dafür Delphi sorgt

Das ist (und war) nicht so. In Delphi muss man alles per Hand Freen (Außer ein paar ausnahmen auf die ich gleich eingehe).

Klassen in Delphi, im Gegensatz zu Objekten, vereinen Konstruktoren und Destruktoren mit dem Alloziieren und Freigeben von Speicher. Das heißt, wenn du Klasse.Create aufrufst wird Speicher gesucht, belegt und eine Inizialisierungsmethode der so genannte Konstruktor wird ausgeführt. Durch das aufrufen des Desturktors (Die Destroy Methode) wird dann zunächst eine Deinitialisierungsfunktion aufgerufen (der Inhalt von Destroy) und danach der Speicher wieder Freigegeben.

Free ist eine Methode welche eigentlich nichts anderes macht als Destroy aufzurufen, allerdings wird vorher einfach noch schnell überprüft ob die Instanz nicht Nil ist (was ansonsten zu einem Fehler führen würde).

Praktisch gilt die Regel für jedes Create muss irgendwo auch ein Free stehen. Für so etwas besonders gut geeignet sind Try-Finally Blöcke.

Code: Alles auswählen

Instanz := Klasse.Create;
try
  // Irgendwas mit Instanz machen
finally
  Instanz.Free
end;

Das führt zunächst den Try block aus, und am ende den Finally block. Wenn im Try Block ein Return kommt (also die Funktion verlassen wird, z.B. durch exit() oder durch einen Fehler) wird der Finally block dennoch ausgeführt. Somit kannst du sicher sein, egal was passiert, am ende ist der Speicher auf jeden fall wieder aufgeräumt. (Denk aber daran, nach dem Free sind alle Daten der Instanz weg)

Es gibt ein paar ausnahmen, in denen du kein Free brauchst. Zum einen ist es Möglich über COM Interfaces Referenzzählung zu implementieren, die diese Aufgabe für dich Übernimmt (ähnlich wie bei Strings oder Arrays, bei denen brauchst du dich auch nicht um den Speicher zu kümmern). Außerdem gibt es in der FCL und LCL die Klasse TCOmponent. Alle Klassen die davon abstammen (so genannte Components) Haben einen Owner. Wenn der Owner gefreed wird kümmert dieser sich automatisch darum alle seine Kinder zu Freen.

Alles was du Im Lazarus Formulareditor verwendest sind Komponenten (also z.B. Buttons, Timer, OpenDialogs, etc.)

Wenn diese Komponenten allerdings keinen Owner zugewiesen bekommen muss man sie auch selbst freen

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Fragen zur Nutzung von Befehl free

Beitrag von kupferstecher »

Was noch nicht gesagt wurde: Beim Beenden des Programmes raeumt das Betriebssystem allen vom Programm angeforderten Speicher auf, d.h. wenn das Programm beendet wurde bleibt auch nichts zurueck, egal ob haendisch "gefreed" wurde oder nicht. Essentiell wird das Aufraemen von Speicher, wenn man waehrend der Laufzeit wiederholt dynamisch Speicher anfordert, d.h. neue Instanzen erzeugt. Das wird dann relevant, wenn dein Programm dann nach einiger Zeit mehr Speicher angefordert hat, wie ueberhaupt verfuegbar ist (Waehrend du praktisch viell nur einen kleinen Teil nutzt).

Was das automatische Aufraeumen angeht: Beim Formular (Form1 z.B.) wird beim Schliessen des Programmes durch das Programm selbst der Destruktor aufgerufen und entsprechend alle Komponenten der GUI (Button, Label etc.), welche die Form als Owner haben, auch 'befreit'. Der Formdesigner setzt den Owner automatisch fuer jedes Control. Es ist also kein weiteres Aufraume noetig.

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Fragen zur Nutzung von Befehl free

Beitrag von Erwin »

Warf hat geschrieben:Alles was du Im Lazarus Formulareditor verwendest sind Komponenten (also z.B. Buttons, Timer, OpenDialogs, etc.)

Wenn diese Komponenten allerdings keinen Owner zugewiesen bekommen muss man sie auch selbst freen

Geht so was überhaupt? Komponente ohne Owner? Bei mir kommt alles auf ein Formular. Bzw. wenn nicht das Elternteil das Formular ist, dann ist das Formular eben der Großelternteil usw.


kupferstecher hat geschrieben:Was das automatische Aufraeumen angeht: Beim Formular (Form1 z.B.) wird beim Schliessen des Programmes durch das Programm selbst der Destruktor aufgerufen und entsprechend alle Komponenten der GUI (Button, Label etc.), welche die Form als Owner haben, auch 'befreit'. Der Formdesigner setzt den Owner automatisch fuer jedes Control. Es ist also kein weiteres Aufraume noetig.

So habe ich es damals in dem einen Buch auch verstanden: So bald das Programm geschlossen wird, wird der Speicher wieder frei gegeben. Habe mir da eigentlich nie so recht Gedanken darüber gemacht, dass der Speicher von einem Programminhalt auch dann belegt sein könnte, wenn das Programm beendet ist. Das wäre dann fast schon so, als würde man von einem Buch nur den Einband zurück geben, aber den Inhalt behalten. Merkwürdige Vorstellung.

Wird auch der Speicher von allem was in dem eigenen Programm abläuft, dann beim beenden frei gegeben? Oder kann man durchaus Codes Schreibe, die Speicher über das Beenden des Programms hinaus belegen?

Auch verstehe ich immer noch nicht, in welchen Fällen Free dann wirklich notwendig ist? Weil ständig jede Kleinigkeit aufzuräumen mag ja vor Jahren wichtig gewesen sein, aber Heute wird ein MB mehr oder weniger doch kaum auffallen? Muss man auch Variablen aufräumen? Ich habe viele Beispielcodes gelesen. 3-4 Bücher. Aber wenn Free mal vorkam (glaube das war nur in einem Buch), dann hießt es eher so was wie: Kann man machen, muss man nicht. Oder: Wenn man es nicht macht, kann man dem Objekt nichts neues mehr zuweisen. Was ich mir bei letzteres aber nicht vorstellen kann, dass das so stimmt.

Hinzu kommt, dass wenn man schon etwas programmiert, also ich zumindest dies auch insgesamt alle Komponenten etc. brauche. In so fern wüsste ich eh nicht, wieso ich mittendrin etwas frei geben sollte? Weil entweder wird dann eh was neues wieder zugewiesen, oder es wird nur vorübergehend nicht gebraucht. Und nur weil man eine Seite (Tab) wechselt, alles auf der nicht aktiven Seite frei zu geben, um dann später wieder neu zu erstellen bzw. zu zuweisen? Das hat für mich eher etwas wie eine Null-Rechnung: Man spart Speicher, aber der PC muss mehr arbeiten.

ich weiß nach wie vor nicht, wie und wo man das Free wirklich Sinnvoll einsetzen soll ... oder gar muss?
Lazarus 2.2.0 / FP 3.2.4

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fragen zur Nutzung von Befehl free

Beitrag von Mathias »

Geht so was überhaupt? Komponente ohne Owner?
Klar geht dies.

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
var
  bt: TButton;
begin
  bt := TButton.Create(nil);
  bt.Parent := self;
end;

Nur hast du mit diesem Code Leichen, wen du kein bt.Free machst.

ich weiß nach wie vor nicht, wie und wo man das Free wirklich Sinnvoll einsetzen soll ... oder gar muss?

Bei Classen, die kein LCL verwenden, zB. TStringList.

Oder gucke mal den Mini-Code an.

Code: Alles auswählen

type
  TMyClass = class(TObject)
    procedure Ausgabe(s: string);
    constructor Create;
    destructor Destroy; override;
  end;
 
  procedure TMyClass.Ausgabe(s: string);
  begin
    WriteLn(s);
  end;
 
  constructor TMyClass.Create;
  begin
    inherited Create;
    WriteLn('Create');
  end;
 
  destructor TMyClass.Destroy;
  begin
    WriteLn('Destroy');
    inherited Destroy;
  end;
 
var
  MyClass: TMyClass;
 
begin
  MyClass := TMyClass.Create;
  MyClass.Ausgabe('Hello ');
  MyClass.Ausgabe('World !');
  MyClass.Free;
end.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Fragen zur Nutzung von Befehl free

Beitrag von Warf »

Erwin hat geschrieben:
Warf hat geschrieben:Alles was du Im Lazarus Formulareditor verwendest sind Komponenten (also z.B. Buttons, Timer, OpenDialogs, etc.)

Wenn diese Komponenten allerdings keinen Owner zugewiesen bekommen muss man sie auch selbst freen

Geht so was überhaupt? Komponente ohne Owner? Bei mir kommt alles auf ein Formular. Bzw. wenn nicht das Elternteil das Formular ist, dann ist das Formular eben der Großelternteil usw.


kupferstecher hat geschrieben:Was das automatische Aufraeumen angeht: Beim Formular (Form1 z.B.) wird beim Schliessen des Programmes durch das Programm selbst der Destruktor aufgerufen und entsprechend alle Komponenten der GUI (Button, Label etc.), welche die Form als Owner haben, auch 'befreit'. Der Formdesigner setzt den Owner automatisch fuer jedes Control. Es ist also kein weiteres Aufraume noetig.

So habe ich es damals in dem einen Buch auch verstanden: So bald das Programm geschlossen wird, wird der Speicher wieder frei gegeben. Habe mir da eigentlich nie so recht Gedanken darüber gemacht, dass der Speicher von einem Programminhalt auch dann belegt sein könnte, wenn das Programm beendet ist. Das wäre dann fast schon so, als würde man von einem Buch nur den Einband zurück geben, aber den Inhalt behalten. Merkwürdige Vorstellung.

Wird auch der Speicher von allem was in dem eigenen Programm abläuft, dann beim beenden frei gegeben? Oder kann man durchaus Codes Schreibe, die Speicher über das Beenden des Programms hinaus belegen?

Auch verstehe ich immer noch nicht, in welchen Fällen Free dann wirklich notwendig ist? Weil ständig jede Kleinigkeit aufzuräumen mag ja vor Jahren wichtig gewesen sein, aber Heute wird ein MB mehr oder weniger doch kaum auffallen? Muss man auch Variablen aufräumen? Ich habe viele Beispielcodes gelesen. 3-4 Bücher. Aber wenn Free mal vorkam (glaube das war nur in einem Buch), dann hießt es eher so was wie: Kann man machen, muss man nicht. Oder: Wenn man es nicht macht, kann man dem Objekt nichts neues mehr zuweisen. Was ich mir bei letzteres aber nicht vorstellen kann, dass das so stimmt.

Hinzu kommt, dass wenn man schon etwas programmiert, also ich zumindest dies auch insgesamt alle Komponenten etc. brauche. In so fern wüsste ich eh nicht, wieso ich mittendrin etwas frei geben sollte? Weil entweder wird dann eh was neues wieder zugewiesen, oder es wird nur vorübergehend nicht gebraucht. Und nur weil man eine Seite (Tab) wechselt, alles auf der nicht aktiven Seite frei zu geben, um dann später wieder neu zu erstellen bzw. zu zuweisen? Das hat für mich eher etwas wie eine Null-Rechnung: Man spart Speicher, aber der PC muss mehr arbeiten.

ich weiß nach wie vor nicht, wie und wo man das Free wirklich Sinnvoll einsetzen soll ... oder gar muss?


Es gibt einen Unterschied zwischen Komponenten und Controls. Controls sind all das Sichtbare auf deiner Form. Diese müssen einen Parent haben (auch ein Control) damit sie sichtbar sind. Komponenten haben aber grundsätzlich nichts mit Forms zu tun. Komponenten sind z.B. auch Timer, welche keinen Parent brauchen. Ein Komponent kann einen Owner haben, muss es aber nicht (z.B. Timer1 := TTimer.Create(Nil) würde einen Timer ohne Owner erstellen).

Owner können im Nachhinein auch nicht geändert werden, parents aber schon.

Und zu der Sache mit dem ob Free sinnvoll ist, ja ist es. Dein Betriebsystem betreibt Paging um zwar jedem Programm vollen Addressraum zu gewährleisten, deinen RAM aber nicht komplett auszufüllen. Somit wird der Addressraum von Anwendungen in Pages aufgeteilt, und Pages die aktuell nicht benötigt werden, werden auf die Festplatte ausgelagert. Eine Page zu laden dauert bis zu einen Faktor tausend länger als wenn die Page im RAM wäre.

Jetzt mal ein kleines gedankenexperiment, Folgendes Szenario: dein OS kann maximal eine Page im RAM haben, eine Page enthält 4 Wörter. Du räumst deinen Speicher nicht auf, und die Pages sehen so aus:

Code: Alles auswählen

OBJEKT1
NOT FREED
NOT FREED
NOT FREED

und

Code: Alles auswählen

OBJEKT2
FREE
FREE
FREE

Not Free bezeichnet ein Objekt was irgendwann alloziiert wurde aber nie gefreed wurde (und daher immer noch Speicher verbraucht) und FREE bezeichnet freien Speicher
Dein code sieht dann so aus:

Code: Alles auswählen

for I:=0 to 10000 do
begin
  Object1.DoSomething(Object2.SomeValue[i]);

jetzt muss für jede Iteration der Schleife entstehen zwei Page Fault, als erstes muss Page1 geladen werden, danach Page2. Obwohl deine Beiden Objekte eigentlich auf eine Page passen würden.

Somit braucht dein Programm statt einer Laufzeit im nanosekunden Bereich plötzlich Sekunden.

Langer rede kurzer Sinn: Verwende Free immer und in jedem Fall.
Es gibt in der FCL die Unit HeapTrc, die sagt dir sobald dein Programm beendet wird, ob du vergessen hast Speicher aufzuräumen, und wenn ja wo.

Dein Argument würde aber stimmen wenn du einen Microcontroller ohne Cache hättest, nur leider ist es bei denen so dass die meistens eher nur im KB-MB beriech an Speicher haben, und daher du Free auf jeden fall brauchst, da sonst selbst das kleinste Programm bereits den gesamten Speicher brauchen würde
Zuletzt geändert von Warf am Sa 18. Nov 2017, 18:30, insgesamt 1-mal geändert.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Fragen zur Nutzung von Befehl free

Beitrag von Mathias »

Es gibt in der FCL die Unit HeapTrc, die sagt dir sobald dein Programm beendet wird, ob du vergessen hast Speicher aufzuräumen, und wenn ja wo
Ich habe die gerade probiert, bei einer LCL-Anwendung, kommen beim beenden des Programms seitenweise Fehler. :roll:

Bei einer einfachen Konsolen-Anwendung, funktioniert es bestens.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

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

Re: Fragen zur Nutzung von Befehl free

Beitrag von Warf »

Mathias hat geschrieben:Ich habe die gerade probiert, bei einer LCL-Anwendung, kommen beim beenden des Programms seitenweise Fehler. :roll:

Bei einer einfachen Konsolen-Anwendung, funktioniert es bestens.


kenn ich, wenn ich anfange mit einem Projekt, und dann erst nach den ersten 2-3 Units denke ich könnte mal HeapTrc verwenden habe ich auch seitenweise Fehler. Meist ist es allerdings nur eine Zeile die sehr oft aufgerufen wird.

Das mit der Konsole könnte allerdings an was anderem liegen. Ohne Formular gibt die Heaptrace Unit ihr Ergebnis im STDOut zurück, wenn du allerdings jetzt ein Terminal verwendest (z.B. Windows Konsole) welches sich direkt nach dem Debuggen schließt, kann es sein das du die Heaptrc ausgabe gar nicht siehst, weil sich das Fenster zu schnell schließt. Das kannst du umgehen indem du über die Funktion SetHeapTarceOutput eine Datei angibst in die der HeapTrace geschrieben werden soll.

Fremdkomponenten können auch für Fehler sorgen. Indy10 z.B. verursacht mit dem Telnet Client einen Memoryblock der Nicht gefreed wird. Das liegt daran, das dieser während initialization initialisiert wird, und bis zum Programm ende bestehen bleibt, daher mit dem Ende des Programmes eh aufgeräumt wird. Für Delphi ReportMemoryLeaksOnShutDown (etwa das selbe wie Heaptrc) hat Indy sogar exceptions eingeführt damit dies nicht reported wird, bei Lazarus muss man es einfach wissen das man es ignorieren kann.

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Fragen zur Nutzung von Befehl free

Beitrag von m.fuchs »

Warf hat geschrieben:Fremdkomponenten können auch für Fehler sorgen. Indy10 z.B. verursacht mit dem Telnet Client einen Memoryblock der Nicht gefreed wird. Das liegt daran, das dieser während initialization initialisiert wird, und bis zum Programm ende bestehen bleibt, daher mit dem Ende des Programmes eh aufgeräumt wird.


Schlecht Stil, das wäre dann ein Ausschlusskriterium für Indy.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

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

Re: Fragen zur Nutzung von Befehl free

Beitrag von Warf »

m.fuchs hat geschrieben:Schlecht Stil, das wäre dann ein Ausschlusskriterium für Indy.

Ich bin auch kein fan von solchen Sachen. Aber grade wenn man einen Server schreibt ist der Thread Manager von Indy nahezu unverzichtbar. Soweit ich weis bieten die anderen Netzwerkkomponenten (z.B. Synapse) keinen eigenen Threadmanager. Und so einen selbst zu schreiben kann ganz schön aufwendig werden, während die Indy implementation verdammt gut ist, und wahrscheinlich besser als das was ich in 1-2 Wochen auf die Beine stellen könnte.

Ansonsten benutze ich Indy auch sehr ungern, da es ungemein kompliziert ist, total überladen, und eigentlich nur für Delphi entwickelt wird und man daher merkt das Lazarus der etwas liblose Port ist.

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Fragen zur Nutzung von Befehl free

Beitrag von Erwin »

Mathias hat geschrieben:Nur hast du mit diesem Code Leichen, wen du kein bt.Free machst.

Leichen die auch dann noch bestehen, wenn das Programm beendet wird?

Was Dein Mini-Code betrifft: Vermute mal, die Methode Free überprüft auch in dem Fall erst mal, ob überhaupt was zum "Zerstören" vorhanden ist, und führt dann den 'destructor' aus? Oder findet es gar destroy direkt?

@ Warf,
Timer1 := TTimer.Crealte(Nil) ... kommt es dabei auf das Nil in Klammern drauf an, dass es keinen Owner hat?
Die Pages, werden die auch unter Linux auf Festplatte ausgelagert? Aber auch sonst bin ich jetzt ... verwirrt. Dachte inzwischen würden die BS (nach Linux auch Win inzwischen) gar jene Programme, die aktuell nicht gebraucht werden, im nicht benutzten RAM-Speicher behalten, damit diese später schneller gestartet werden können.
Nutze Free gar nicht, weil es auch keines der Bücher mir es beigebracht hatte. Hatte bis jetzt dennoch nicht den Eindruck, dass irgendwo der Speicher überlaufen würde.?

Wäre es denn also sogar anzuraten, auch jene Komponenten, StringList, StringGrid etc. frei zu geben, die im Augenblick nicht vom Anwender genutzt wird?
Also zum Beispiel ein Programm, das mittels TabsControl (?), Panel etc. mehre Seiten darstellen tut. Auf Seite 1 ist ein StringGrid mit Daten. Auf Seite 2 ein Bild. Seite 3 Memo. Wäre es dann Sinnvoll, wenn der Anwender von Seite 1 auf 2 Wechselt, den Speicher von StringGrid (und was sonst noch auf der optischen Seite ist) frei zu geben. usw. usf.?


Danke euch allen. Immerhin lerne ich immer mehr dazu. Schade bzw. Ärgerlich dass es in den Büchern, die ich zu/für Delphi, und FPC & Lazrarus, gekauft habe, Free genau genommen gar nicht so recht behandelt wird. Und das obwohl es scheinbar ein wichtiger Befehl ist, der obendrein etwas schwer (intuitiv) sinnvoll einzusetzen ist.
Lazarus 2.2.0 / FP 3.2.4

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2636
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: Fragen zur Nutzung von Befehl free

Beitrag von m.fuchs »

Erwin hat geschrieben:
Mathias hat geschrieben:Nur hast du mit diesem Code Leichen, wen du kein bt.Free machst.

Leichen die auch dann noch bestehen, wenn das Programm beendet wird?

Das ist doch unerheblich. Wenn man etwas erzeugt hat, ist man auch dafür verantwortlich es wieder wegzuräumen. Das sollte man immer machen.

Erwin hat geschrieben:Timer1 := TTimer.Crealte(Nil) ... kommt es dabei auf das Nil in Klammern drauf an, dass es keinen Owner hat?

Genau. Wenn da eine Komponente (also nicht nil) übergeben wird, würde sich der TTimer dort anmelden. Und damit auch dafür sorgen, dass es später automatisch entsorgt wird.

Erwin hat geschrieben:Wäre es denn also sogar anzuraten, auch jene Komponenten, StringList, StringGrid etc. frei zu geben, die im Augenblick nicht vom Anwender genutzt wird?
Also zum Beispiel ein Programm, das mittels TabsControl (?), Panel etc. mehre Seiten darstellen tut. Auf Seite 1 ist ein StringGrid mit Daten. Auf Seite 2 ein Bild. Seite 3 Memo. Wäre es dann Sinnvoll, wenn der Anwender von Seite 1 auf 2 Wechselt, den Speicher von StringGrid (und was sonst noch auf der optischen Seite ist) frei zu geben. usw. usf.?

Nein, wenn diese Komponenten einen Owner haben, dann solltest du dem auf keinen Fall dazwischengrätschen. Räum nur das weg, was du selber erzeugt hast. Aber nicht die Sachen, die im Formulareditor entworfen werden.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

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

Re: Fragen zur Nutzung von Befehl free

Beitrag von Warf »

Erwin hat geschrieben:Timer1 := TTimer.Crealte(Nil) ... kommt es dabei auf das Nil in Klammern drauf an, dass es keinen Owner hat?
Die Pages, werden die auch unter Linux auf Festplatte ausgelagert? Aber auch sonst bin ich jetzt ... verwirrt. Dachte inzwischen würden die BS (nach Linux auch Win inzwischen) gar jene Programme, die aktuell nicht gebraucht werden, im nicht benutzten RAM-Speicher behalten, damit diese später schneller gestartet werden können.
Nutze Free gar nicht, weil es auch keines der Bücher mir es beigebracht hatte. Hatte bis jetzt dennoch nicht den Eindruck, dass irgendwo der Speicher überlaufen würde.?

Wäre es denn also sogar anzuraten, auch jene Komponenten, StringList, StringGrid etc. frei zu geben, die im Augenblick nicht vom Anwender genutzt wird?
Also zum Beispiel ein Programm, das mittels TabsControl (?), Panel etc. mehre Seiten darstellen tut. Auf Seite 1 ist ein StringGrid mit Daten. Auf Seite 2 ein Bild. Seite 3 Memo. Wäre es dann Sinnvoll, wenn der Anwender von Seite 1 auf 2 Wechselt, den Speicher von StringGrid (und was sonst noch auf der optischen Seite ist) frei zu geben. usw. usf.?


Danke euch allen. Immerhin lerne ich immer mehr dazu. Schade bzw. Ärgerlich dass es in den Büchern, die ich zu/für Delphi, und FPC & Lazrarus, gekauft habe, Free genau genommen gar nicht so recht behandelt wird. Und das obwohl es scheinbar ein wichtiger Befehl ist, der obendrein etwas schwer (intuitiv) sinnvoll einzusetzen ist.


Zu den Pages, ja auch Linux verwendet Pages, jedes General Purpose Betriebsystem was Multithreading unterstützt verwendet Paging. Das liegt daran das das Betriebsystem jedem Programm das Läuft vorgaukelt es hätte den vollen Addressraum. Bei 32 Bit wären das Adresse 1 (Adresse 0 wird nicht verwendet) bis Addrese 4.294.967.295. Also kann jedes Programm theoretisch bis zu 4 GB Speicher belegen. Wenn man sich jetzt überlegt das ein Rechner eventuell nur 8GB RAM hat, für gewöhnlich mehr als 1 Programm gleichzeitig läuft und das Betriebsystem ja auch noch Speicher braucht, merkt man schnell das wenn jedes Programm echten Physischen Speicher addressieren würde das ganze nicht aufgehen würde. Daher macht das Betriebsystem es so: Alles was im Kernel läuft verwendet echte Physikalische Adressen (diese werden dann über Funktionen wie kmalloc im linux kernel allozieret). Wenn ein Programm jetzt versucht eine Speicheradresse zu referenzierten schaut das Betriebsystem einfach nach welche Adresse ist das, und schaut dann in der Pagetable nach. Sagen wir die Adresse wäre Adresse 3215. Eine Page belegt 1024 Byte (keine Ahnung was Linux aktuell verwendet, das letzte mal als ich das gelernt hab ist schon 2 Jahre her). Das Betriebsystem schaut jetzt also Adresse 3215 div 1024 ist 3 (integer Division) und 3215 mod 1024 ist 143. Also ist die Angeforderte Adresse auf Page 3 Eintrag Nummer 143. Wenn page 3 des Programmes jetzt nicht im RAM ist, haben wir eine PageFault, und dann wird nach einem bestimmten Algorithmus ausgewählt welche Page rausgeworfen wird um die Neue zu laden.
Dieser Algorithmus ist meist sehr simpel, da das Betriebsystem sehr schnell laufen muss kann da nicht viel gerechnet werden. Meist wird aber immer die Page rausgeworfen die seit der letzen pagefault die wenigsten aufrufe hatte (Der linux kernel selbst implementiert 4 oder 5 Algorithmen dafür, welcher aktuell der Standard ist keine Ahnung).

Wenn ein Programm jetzt Speicher anfordert schaut das Betriebsystem im gesamten Adressraum nach passendem Speicher. Auch hier sind die Algorithmen sehr simpel. Linux verwendet glaube ich eine Kombination aus First Fit (erstes freies Feld verwenden) und Best fit (versuchen so wenig Speicher wie möglich zu wasten). Auch wenn man keine Assumptions darüber machen sollte, kann man doch davon ausgehen dass der Speicher also von Oben nach unten gefüllt wird (Wo oben und wo unten ist kommt natürlich auch auf den Kernel an). Wenn du also regelmäßig deinen Speicher aufräumst sind die Chancen am besten das alles auf den ersten paar pages landet, und du nicht so viele verschiedene Pages hast die zwischendurch geswapt werden müssen.

Diese QuickStart Mechanik (also Anwendungen im RAM lassen) ist nicht direkt im RAM. Der Programmcode selbst befindet sich ja auch nur auf den Pages. Das Heist Linux bereitet zwar diese Pages vor, kann sie aber jederzeit auf die Festplatte auslagern falls das nötig sein sollte. Der Vorteil ist aber, da die Pages schon erstellt sind, selbst wenn sich alle auf der Festplatte befinden sollten, fällt der ganze Overhead des erstellen dieser Strukturen weg. Und wenn nicht Ballzuviel Speicher benötigt wird (also z.B. nichts anderes auf dem Rechner aktuell läuft) bleiben diese Pages ja auch im RAM. sie werden ja nur rausgeworfen falls der Speicher wirklich benötigt wird. Außerdem wird zu diesem Zeitpunkt nur sehr wenige Pages benötigt. Wenn ein Programm startet wird lediglich die Page benötigt die den Programmstart Code enthält (also die mit dem entry Point), der Erste Stack Frame, und eventuell ein paar globale Variablen. Wirklich viele Pages werden ja erst verwendet wenn viel Speicher verwendet wurde.

Irgendwann wenn ein Programm immer wieder neuen Speicher anfordert und freigibt, sieht der Speicher halt aus wie ein Schweizer käse mit vielen Löchern drin. Das nennt man Fragmentierung. Und erst dann wird as Paging ein Problem. Solange das Programm noch ganz am Anfang ist braucht es fast keine Pages. Neue Pages werden auch erst erstellt wenn der Speicher benötigt wird, daher ist das relativ billig zu realisieren. Wenn du jetzt allerdings nur Speicher alloziierst aber nie frei gibst, kannst du dir vorstellen das die Fragmentierung nur extremer werden kann.


Das mit dem Speicher freigeben wenn er nicht benötigt wird ist so eine Sache für sich. Speicher anfordern und freigeben kann länger dauern als ein PageFault. Wenn du Speicher alloziierst wird der aktuelle Prozess pausiert, von der CPU genommen, das Betriebsystem läd einen eigenen Prozess, der die alloziiereung durchführt. Wenn dieser fertig ist wird wieder das Programm auf die CPU geladen. Diesen Prozesswechsel nennt man Kontextwechsel, und der ist verdammt teuer. Vor allem da bei diesem Kontextwechsel der Prozessscheduler anspringt und eventuell gar nicht dein Programm wieder reinläd, sondern erst mal ein anderes Programm auswählt, was jetzt CPU Zeit bekommt. Das wiederum kann dazu führen das das Betriebsystem deine Programmpages aus dem Ram wirft, und dann hast du einen Kontextwechsel und eine Page Fault.

Das solltest du höchstens machen wenn die Komponenten sehr viel Speicher brauchen (also z.B. ein Memo was mehrere MB bis GB an Text enthält)


Wie du vielleicht schon merkst ist das Thema nicht so simpel (einfach aus dem Grund das Betriebsysteme nicht so simpel sind :D). Falls du dich wirklich dafür interessierst, wissen willst wie genau sich das verhält und auch wie Windows oder Linux das ganze umsetzen, kann ich dir das Buch Modern Operating Systems von Andrew S. Tanenbaum empfehlen. Die Quasi Standardlektüre wenn es um Betriebsysteme geht. In diesem Buch werden auch viele Beispiele aus dem BSD, Linux oder Windows Kernel angeführt. Hoch interessant, und dieses Jahr ist auch eine Neue Auflage rausgekommen, die sollte auch up to date sein mit der Aktuellen Technologie im Linux und Windows Kernel. Ist aber auf englisch (es gibt aber leider kein vergleichbares Werk auf Deutsch).

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Fragen zur Nutzung von Befehl/Methode free

Beitrag von Erwin »

Ok, mal sehen, ob ich es jetzt langsam begriffen habe:
m.fuchs hat geschrieben:Genau. Wenn da eine Komponente (also nicht nil) übergeben wird, würde sich der TTimer dort anmelden. Und damit auch dafür sorgen, dass es später automatisch entsorgt wird.

Automatisch entsorgt wird? Also beim beenden des Programms? Dann geht es wohl vor allem darum, sicher zu stellen, dass nicht das Programm es versehentlich übersieht? Kommt so was öfter bei selbst erstelltem vor? Oder ist es eben Aufwendiger (und deshalb mit Fehler zu rechnen) weil der Formulareditor nicht richtig dabei mit einbezogen7informiert wurde?

m.fuchs hat geschrieben:Nein, wenn diese Komponenten einen Owner haben, dann solltest du dem auf keinen Fall dazwischengrätschen. Räum nur das weg, was du selber erzeugt hast. Aber nicht die Sachen, die im Formulareditor entworfen werden.

Mit erzeugen ist wohl in dem Fall gemeint, selbst erstellt. Nicht einfach aus der Komponenten-Palette was auf das Formular etc. erstellt zu haben? Und das bei der Erstellung kein Elternkomponente zugewiesen bekam?

Alles andere aus der Komponentenliste sollte man dann dem Programm/Formulareditor überlassen?
Zählt dazu auch einfache eigene Prozeduren und Funktionen?
Und wie sieht es mit Variablen, Arrays etc. aus?
StringList soll man ja wiederum selbst aufräumen, wenn ich das richtig verstanden habe? Anderseits gibt es dazu ja auch keine Komponente in der K-Leiste.


@ Warf, das ist ja irgendwie verwirrend, was da abgeht. Leider kann ich kaum Englisch. Und Technik-Englisch ... das dann gleich schon gar nicht.
Wenn ich Dich also richtig verstehe, sollte man mit der Nutzung von Free abschätzen, ob man die Komponente bald wieder braucht oder es länger dauert. Wenn man es bald wieder braucht, dann lieber vorerst stehen lassen. Nur wenn es länger dauert, sollte man Free nutzen. Und beim Programm beenden natürlich. So wie es bei den einen Spezielle Glühbirne, die beim Einschalten gleich dermaßen viel Strom brauchen, wofür eine normale eine Stunde davon leuchten könnte. Die Spezielle dann auszuschalten, wenn man in 10 Minuten eh wieder anmachen muss, würde da dann kein Sinn ergeben. Und Free für eine Komponenten (bzw. dessen Freigabe) zu nutzen, die man bald eh wieder braucht, würde für das BS-Speicher-System kein Sinn machen?
Lazarus 2.2.0 / FP 3.2.4

Antworten