Create and forget: Referenzzählung für Objekte
- Jorg3000
- Lazarusforum e. V.
- Beiträge: 359
- Registriert: So 10. Okt 2021, 10:24
- OS, Lazarus, FPC: Win64
- Wohnort: NRW
Create and forget: Referenzzählung für Objekte
Moin!
Jedes Objekt, das man erzeugt, muss man in Pascal manuell wieder freigeben, ist mein bisheriger Kenntnisstand.
Ich habe mal über einen Lösungsansatz per IInterface-Referenzzählung gelesen, konnte damit aber spontan wenig anfangen. War mir zu undurchsichtig.
Und zu Delphi hatte ich als Phrase gefunden: "Automatic reference counting (ARC) for classes is supported by LLVM-based Delphi compilers", aber dazu bislang keine weiteren Details.
Stattdessen habe ich mir nun eigene Gedanken zu Referenzzählung für Objekte gemacht ... und dabei herausgekommen ist eine Unit von mir, mit Beispielprojekt und einer kleinen Webseite ...
https://www.html-file.org/pascal/reference-counting/de/
Sourcecode siehe auf Unterseite "Dokumentation und Beispiel".
EDIT: Update der Unit, siehe hier Forum Seite 2
Grüße, Jörg
Jedes Objekt, das man erzeugt, muss man in Pascal manuell wieder freigeben, ist mein bisheriger Kenntnisstand.
Ich habe mal über einen Lösungsansatz per IInterface-Referenzzählung gelesen, konnte damit aber spontan wenig anfangen. War mir zu undurchsichtig.
Und zu Delphi hatte ich als Phrase gefunden: "Automatic reference counting (ARC) for classes is supported by LLVM-based Delphi compilers", aber dazu bislang keine weiteren Details.
Stattdessen habe ich mir nun eigene Gedanken zu Referenzzählung für Objekte gemacht ... und dabei herausgekommen ist eine Unit von mir, mit Beispielprojekt und einer kleinen Webseite ...
https://www.html-file.org/pascal/reference-counting/de/
Sourcecode siehe auf Unterseite "Dokumentation und Beispiel".
EDIT: Update der Unit, siehe hier Forum Seite 2
Grüße, Jörg
Zuletzt geändert von Jorg3000 am Di 26. Apr 2022, 08:33, insgesamt 1-mal geändert.
-
- Beiträge: 726
- Registriert: Do 27. Sep 2012, 00:07
- OS, Lazarus, FPC: Win10Pro-64Bit, Immer letzte Lazarus Release mit SVN-Fixes
- CPU-Target: x86_64-win64
- Wohnort: Hamburg
Re: Create and forget: Referenzzählung für Objekte
Nein, nur für Objekte die kein Eigentümer(Owner) haben, wie z.B. TList, TStringList. Objekte mit Eigentümer sieht man an dem Konstruktor:Jorg3000 hat geschrieben: So 24. Apr 2022, 09:51 Moin!
Jedes Objekt, das man erzeugt, muss man in Pascal manuell wieder freigeben, ist mein bisheriger Kenntnisstand.
...
Code: Alles auswählen
constructor Create(TheOwner: TComponent);
Ich gebe in meinem Programmen Objekte nach Benutzung frei, das würde ich auch jedem empfehlen.
Diese ganze Referenzzählung und automatische Freigabe haben unter anderem dazu geführt, dass wir trotz Intel Core i5/i7 mit 16 GB RAM mehrere Kerne lahme Programme haben.
Vergleiche Office 2000/2002 mit aktuellem Office.
-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Create and forget: Referenzzählung für Objekte
Im englischen Forum gab es vor nicht allzulanger zeit mal einen Beitrag mit einer Implementierung die ganz gut aussah: https://forum.lazarus.freepascal.org/in ... 306.0.html
Grundsätzlich gibt es theoretisch 2 Möglichkeiten Referenzzählung zu Implementieren, COM interfaces die von haus aus referenzgezählt sind, oder Managed Records bei denen man das Assignment sowie Initialisierung und Finalisierung selbst überladen kann.
Allerdings ist meines wissens nach https://gitlab.com/freepascal.org/fpc/s ... sues/37164 immer noch nicht gefixt, weshalb Managed records noch nicht wirklich benutzbar sind. Da muss man leider auf Interfaces Zurückgreifen, was etwas doof ist, da die ein zwischenobjekt bilden, sodass man immer 2 mal Pointer dereferenzieren muss (durch virtuelle Methoden wahrscheinlich sogar 3) statt 1 mal, für jeden zugriff, und dass immer 2 Create und Free aufgerufen werden mussen.
Alternativ, kannst du einfach wo möglich Interfaces benutzen, die erlauben auch noch andere coole sachen wie Operator überladen. Schönes beispiel dafür ist die GMP:
Das doofe dabei ist leider nur das du praktisch für jede Klasse ein interface erstellen musst (ich wollte irgendwann mal tatsächlich einen code generator schreiben der das automatisch macht) aber wenn du dann nur über dieses Interface zugreifst musst du dich gar nicht mehr um Memory Management kümmern.
Ich benutze Interfaces generell häufiger an stellen an denen es einfach keine Sinnige ownership beziehung gibt. Wenn ein objekt tatsächlich von mehreren Objekten verwendet wird und nicht klar ist welches objekt davon am längsten lebt, ist das der einfachste weg.
Ansonsten sollte man immer versuchen zu überlegen wer der tatsächliche owner eines Objekts ist. Und fest definierte ownership übergaben zu implementieren falls der Owner wechseln sollte. Aber das ist leider nicht immer möglich. Beispiel, in einem (ungerichtet)zyklischen Graphen wo ein Knoten mehrere vorgängerknoten haben kann ist das nicht unbedingt klar, hier muss man eigentlich immer zählen wie viele eltern knoten es gibt und wenn die Zahl auf 0 fällt, und der Knoten unereichbar ist, kann er gefreed werden. Da ist Referenzzählung eine sehr gute option (wobei das eventuell duch tiefliegende Zyklen auch problematisch werden kann)
Vor allem das Office langsamer geworden ist wegen Referenzzählung halte ich für ziemlich unwahrscheinlich, ich kenne zwar die Code-Basis von Office nicht, würde aber mal davon ausgehen das a. der Kern immernoch gleich ist, und b. die Performanceprobleme vor allem von der massiven anzahl neuer Features die seit dem dazu kamen kommt. Office 2000 hat nur einen Bruchteil der Funktionalität moderner Office versionen.
Hier ist auch der Vergleich zu Oberon schön, der neusten Sprache von Wirth, Oberon ist Garbage Collected (und damit hat es massiv höhere Performance Ansprüche als Referenzzählung) und trozdem ist die Oberon umgebung, die gleichzeitig ein komplettes Betriebsystem ist, extrem schnell. Der Grund dafür ist das die keinen großen Schnick Schnack drin hat, sonder einfach nur minimale Kernfunktionalität, und siehe da, Performance ist absolut kein Problem.
Ich möchte dran erinnern das der FPC und Delphi seit Jahrzehnten Referenzzählung bei Strings und Arrays macht, und ich würde mal ganz dreist behaupten das Strings mehr verwendet werden als Objektpointer.
Das Faszinierende wenn dieses Thema aufkommt finde ich ist das es viele gibt die der Meinung sind Referenzgezählte Objekte braucht man nicht, aber ich noch nie einen Pascal Programmierer gefunden habe der der Meinung ist das Arrays und Strings schlecht sind und stattdessen C-Style Pointer wie PChar verwendet.
MMn. gibt es absolut keinen Grund warum man Free per Hand aufrufen müsste. Strings und Arrays machen es vor, und absolut niemand beschwert sich hier. Wenn man massive performance braucht, sodass Referenzzählung tatsächlich den Unterschied macht (ich war schon einmal an diesem Punkt) sollte man eh keine Klassen benutzen, da die Ganzen Vorteile von klassen (Inheritance, Virtuelle Methoden, RTTI, etc.) alle samt nicht auf high performance ausgelegt sind. Dann sind Records mit cutom Allocatoren eh was man eigentlich machen will
Grundsätzlich gibt es theoretisch 2 Möglichkeiten Referenzzählung zu Implementieren, COM interfaces die von haus aus referenzgezählt sind, oder Managed Records bei denen man das Assignment sowie Initialisierung und Finalisierung selbst überladen kann.
Allerdings ist meines wissens nach https://gitlab.com/freepascal.org/fpc/s ... sues/37164 immer noch nicht gefixt, weshalb Managed records noch nicht wirklich benutzbar sind. Da muss man leider auf Interfaces Zurückgreifen, was etwas doof ist, da die ein zwischenobjekt bilden, sodass man immer 2 mal Pointer dereferenzieren muss (durch virtuelle Methoden wahrscheinlich sogar 3) statt 1 mal, für jeden zugriff, und dass immer 2 Create und Free aufgerufen werden mussen.
Alternativ, kannst du einfach wo möglich Interfaces benutzen, die erlauben auch noch andere coole sachen wie Operator überladen. Schönes beispiel dafür ist die GMP:
Code: Alles auswählen
var a, b: MPInteger;
begin
a := 1024; // erstellt ein neues GMP objekt und setzt den wert 1024
b := a ** a; // erstellt ein neues GMP objekt und setzt den wert auf a^a
ShowMessage(b); // konvertiert b nach string
// weil a und b Interfaces sind werden sie automatisch gefreed
end;
Ich benutze Interfaces generell häufiger an stellen an denen es einfach keine Sinnige ownership beziehung gibt. Wenn ein objekt tatsächlich von mehreren Objekten verwendet wird und nicht klar ist welches objekt davon am längsten lebt, ist das der einfachste weg.
Ansonsten sollte man immer versuchen zu überlegen wer der tatsächliche owner eines Objekts ist. Und fest definierte ownership übergaben zu implementieren falls der Owner wechseln sollte. Aber das ist leider nicht immer möglich. Beispiel, in einem (ungerichtet)zyklischen Graphen wo ein Knoten mehrere vorgängerknoten haben kann ist das nicht unbedingt klar, hier muss man eigentlich immer zählen wie viele eltern knoten es gibt und wenn die Zahl auf 0 fällt, und der Knoten unereichbar ist, kann er gefreed werden. Da ist Referenzzählung eine sehr gute option (wobei das eventuell duch tiefliegende Zyklen auch problematisch werden kann)
Das ist eine ziemlich steile These. Klar, wenn man in einem Inneren loop assignments hat, aber wir reden hier von einem Increment, Dekrement und einer if-abfrage pro assignment, also wenn den programm nicht nur aus assignments besteht glaube ich kaum das das einen bemerkbaren Effekt hat.Soner hat geschrieben: So 24. Apr 2022, 11:25 Diese ganze Referenzzählung und automatische Freigabe haben unter anderem dazu geführt, dass wir trotz Intel Core i5/i7 mit 16 GB RAM mehrere Kerne lahme Programme haben.
Vergleiche Office 2000/2002 mit aktuellem Office.
Vor allem das Office langsamer geworden ist wegen Referenzzählung halte ich für ziemlich unwahrscheinlich, ich kenne zwar die Code-Basis von Office nicht, würde aber mal davon ausgehen das a. der Kern immernoch gleich ist, und b. die Performanceprobleme vor allem von der massiven anzahl neuer Features die seit dem dazu kamen kommt. Office 2000 hat nur einen Bruchteil der Funktionalität moderner Office versionen.
Hier ist auch der Vergleich zu Oberon schön, der neusten Sprache von Wirth, Oberon ist Garbage Collected (und damit hat es massiv höhere Performance Ansprüche als Referenzzählung) und trozdem ist die Oberon umgebung, die gleichzeitig ein komplettes Betriebsystem ist, extrem schnell. Der Grund dafür ist das die keinen großen Schnick Schnack drin hat, sonder einfach nur minimale Kernfunktionalität, und siehe da, Performance ist absolut kein Problem.
Ich möchte dran erinnern das der FPC und Delphi seit Jahrzehnten Referenzzählung bei Strings und Arrays macht, und ich würde mal ganz dreist behaupten das Strings mehr verwendet werden als Objektpointer.
Das Faszinierende wenn dieses Thema aufkommt finde ich ist das es viele gibt die der Meinung sind Referenzgezählte Objekte braucht man nicht, aber ich noch nie einen Pascal Programmierer gefunden habe der der Meinung ist das Arrays und Strings schlecht sind und stattdessen C-Style Pointer wie PChar verwendet.
MMn. gibt es absolut keinen Grund warum man Free per Hand aufrufen müsste. Strings und Arrays machen es vor, und absolut niemand beschwert sich hier. Wenn man massive performance braucht, sodass Referenzzählung tatsächlich den Unterschied macht (ich war schon einmal an diesem Punkt) sollte man eh keine Klassen benutzen, da die Ganzen Vorteile von klassen (Inheritance, Virtuelle Methoden, RTTI, etc.) alle samt nicht auf high performance ausgelegt sind. Dann sind Records mit cutom Allocatoren eh was man eigentlich machen will
- Jorg3000
- Lazarusforum e. V.
- Beiträge: 359
- Registriert: So 10. Okt 2021, 10:24
- OS, Lazarus, FPC: Win64
- Wohnort: NRW
Re: Create and forget: Referenzzählung für Objekte
Hi!
Danke für die Antworten!
Meine Lösung per Managed Record (siehe meine Webseite) hat kein Problem mit dem Bug der doppelten Finalisierung.
Ich möchte auch nichts an Objekten ändern, die einen Owner haben und wo das Freigeben quasi automatisch und problemlos funktioniert.
Meine Unit ist auch kein Performance-Fresser. Nach meiner Vorstellung braucht die Zählung nur für bestimmte Objekte verwendet werden, wo ein manuelles Free schwierig wird. Habe ich auf meiner Webseite erklärt.
Und für ein lahmes Microsoft Office kann ich nun wirklich nichts.
Vielleicht gibt es noch jemanden, der trotzdem Bedarf an Referenzzählung in bestimmten Fällen hätte, und mein Beispielprojekt ausprobieren möchte.
Grüße, Jörg
Danke für die Antworten!
Meine Lösung per Managed Record (siehe meine Webseite) hat kein Problem mit dem Bug der doppelten Finalisierung.

Ich möchte auch nichts an Objekten ändern, die einen Owner haben und wo das Freigeben quasi automatisch und problemlos funktioniert.
Meine Unit ist auch kein Performance-Fresser. Nach meiner Vorstellung braucht die Zählung nur für bestimmte Objekte verwendet werden, wo ein manuelles Free schwierig wird. Habe ich auf meiner Webseite erklärt.
Und für ein lahmes Microsoft Office kann ich nun wirklich nichts.

Vielleicht gibt es noch jemanden, der trotzdem Bedarf an Referenzzählung in bestimmten Fällen hätte, und mein Beispielprojekt ausprobieren möchte.
Grüße, Jörg
Zuletzt geändert von Jorg3000 am So 24. Apr 2022, 18:15, insgesamt 1-mal geändert.
-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Create and forget: Referenzzählung für Objekte
Das problem ist das du das nicht garantieren kannst. Dieser Doppelte Finalisierungs bug habe ich nur bei Konstruktoren und bei GetEnumerator festgestellt (das heist aber nicht das er ausschließlich da auftritt). Du kannst zwar sagen das du die nicht benutzt, aber was ist wenn der Record in einem anderen record verwendet wird der das benutzt?Jorg3000 hat geschrieben: So 24. Apr 2022, 14:13 Danke für die Antworten!
Meine Lösung per Managed Record (siehe meine Webseite) hat kein Problem mit dem Bug der doppelten Finalisierung.![]()
Das ist das Problem bei diesem Bug, der kann dir irgendwann an einer ganz anderen stelle um die Ohren fliegen, in einer ganz anderen Unit, in einem ganz anderen typen, weil das ganze von einem anderen typen verwendet wird in einer Funktion die diesen Bug auslöst. Es würde halt vorraussetzen das nicht nur dein typ, sondern jeder der deinen Typen irgendwann verwendet sowie jeder der einen typen der deinen typen irgendwann verwendet, etc. nie in diesen Bug läuft.
Vor allem ist nicht klar wo er überall auftritt, bei Konsturktoren und Enumeratoren scheint es immer der Fall zu sein, aber wie sieht es aus mit anderen situationen, eventuell ist das sogar vom Optimierungslevel oder inlining abbhängig, sodass der Code problemlös läuft, aber man dann eine Zeile wo ganz anders hinzufügt, wodurch sich inlining ändert und plötzlich kracht es.
Dieser bug ist extrem gefährlich. Von daher sind Managed records (die sich auf Finalise verlassen) bis dieser bug gefixt komplett unbenutzbar, da du nie wissen kannst wie der record am ende benutzt wird, oder wann er auftritt.
- Jorg3000
- Lazarusforum e. V.
- Beiträge: 359
- Registriert: So 10. Okt 2021, 10:24
- OS, Lazarus, FPC: Win64
- Wohnort: NRW
Re: Create and forget: Referenzzählung für Objekte
Wie gesagt, das Problem gibt es in meiner Unit nicht.
Denn egal wie oft die Finalisierung auf mein Record ausgeführt wird, wirkt es sich nicht negativ aus, da ich bereits beim ersten Finalize im Record alles brav auf nil setze.
Wir brauchen hier kein Problem breitzutreten, das meine Unit gar nicht betrifft.
Grüße, Jörg
Denn egal wie oft die Finalisierung auf mein Record ausgeführt wird, wirkt es sich nicht negativ aus, da ich bereits beim ersten Finalize im Record alles brav auf nil setze.
Wir brauchen hier kein Problem breitzutreten, das meine Unit gar nicht betrifft.
Grüße, Jörg
-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Create and forget: Referenzzählung für Objekte
Ich glaub du verstehst den bug nicht so ganz, es ist nicht so das am ende zwei finalise durchgeführt werden, sondern das ein finalise durchgeführt wird bevor die daten noch verwendet werden. D.h. du benutzt das teil, und während noch referenzen darauf sind wird es auf nil gesetzt wird:
Beispiel:
Wirft ne Access violation, obwohl data auf nil gesetzt wird und auf nil getestet wird.
Langer Rede kurzer Sinn, managed records sind aktuell unbenutzbar
Beispiel:
Code: Alles auswählen
{ TTest }
TTest = record
Data: PInteger;
constructor Create(AData: Integer);
class operator Finalize(var t: TTEst);
end;
{ TTest }
constructor TTest.Create(AData: Integer);
begin
New(Data);
Data^:=AData;
end;
class operator TTest.Finalize(var t: TTEst);
begin
if not Assigned(t.Data) then
Exit;
Dispose(t.Data);
t.Data := nil;
end;
var
t: TTest;
begin
t := TTest.Create(42);
WriteLn(t.Data^);
Langer Rede kurzer Sinn, managed records sind aktuell unbenutzbar
- Jorg3000
- Lazarusforum e. V.
- Beiträge: 359
- Registriert: So 10. Okt 2021, 10:24
- OS, Lazarus, FPC: Win64
- Wohnort: NRW
Re: Create and forget: Referenzzählung für Objekte
Aber ich benutze keinen Record-constructor Create, sondern einen class operator Initialize.
In meinem Beispiel gibt es keine Access Violation - und das Beispiel funktioniert ganz hervorragend
... auch dort, wo der Record als lokale Variable verwendet wird (wo der Speicherbereich nicht vorher mit Nullen gefüllt wird).
Hast du es mit meinem Beispiel ausprobiert?
Danke für deine Hinweise und Bedenken, aber bitte keine Probleme behaupten, die nichts mit meiner Unit zu tun haben.
Grüße, Jörg
In meinem Beispiel gibt es keine Access Violation - und das Beispiel funktioniert ganz hervorragend

... auch dort, wo der Record als lokale Variable verwendet wird (wo der Speicherbereich nicht vorher mit Nullen gefüllt wird).
Hast du es mit meinem Beispiel ausprobiert?
Danke für deine Hinweise und Bedenken, aber bitte keine Probleme behaupten, die nichts mit meiner Unit zu tun haben.
Grüße, Jörg
-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Create and forget: Referenzzählung für Objekte
Das problem ist, du magst create nicht benutzen, aber sobald irgendjemand einen record benutzt der deinen record benutzt, und da ein create benutzt geht das genauso kaputt:
TTest benutzt keine konstruktor, wird aber in TTest2 benutzt, trozdem geht es kaputt
Code: Alles auswählen
{ TTest }
TTest = record
Data: PInteger;
class operator Finalize(var t: TTest);
class operator Initialize(var t: TTest);
class operator AddRef(var t: TTest);
class operator Copy(constref src: TTest; var dst: TTest);
end;
{ TTest2 }
TTest2 = record
Data: TTest;
constructor Create(AData: Integer);
end;
{ TTest2 }
constructor TTest2.Create(AData: Integer);
begin
Data.Data := GetMem(SizeOf(Integer) * 2);
Data.Data[0] := 1; // Ref counting
Data.Data[1] := AData;
end;
{ TTest }
class operator TTest.Finalize(var t: TTest);
begin
if Assigned(t.Data) then
begin
Freemem(t.Data);
t.Data := nil;
end;
end;
class operator TTest.Initialize(var t: TTest);
begin
t.Data:=nil;
end;
class operator TTest.AddRef(var t: TTest);
begin
if Assigned(t.Data) then
begin
Inc(t.Data[0]);
end;
end;
class operator TTest.Copy(constref src: TTest; var dst: TTest);
begin
if Assigned(dst.Data) then
begin
dec(dst.Data[0]);
if dst.Data[0] <= 0 then
Freemem(dst.Data);
dst.Data := src.Data;
if Assigned(dst.Data) then
Inc(dst.Data[0]);
end;
end;
var
t: TTest2;
begin
t := TTest2.Create(42);
WriteLn(t.Data.Data[1]);
end.
- Jorg3000
- Lazarusforum e. V.
- Beiträge: 359
- Registriert: So 10. Okt 2021, 10:24
- OS, Lazarus, FPC: Win64
- Wohnort: NRW
Re: Create and forget: Referenzzählung für Objekte
Na ja gut, dann biete ich meine Unit mit dem Hinweis an ...
Betten Sie den Referenzzähler-Record derzeit nicht einen anderen Record ein, welcher Create benutzt.
... bis dieser fpc-Bug behoben ist https://gitlab.com/freepascal.org/fpc/s ... sues/37164
Mit dieser Einschränkung kann ich leben, solange meine Unit selbst keinen Bug hat und außerhalb des o.g. Sonderfalls richtig funktioniert.
Grüße, Jörg
Betten Sie den Referenzzähler-Record derzeit nicht einen anderen Record ein, welcher Create benutzt.

... bis dieser fpc-Bug behoben ist https://gitlab.com/freepascal.org/fpc/s ... sues/37164
Mit dieser Einschränkung kann ich leben, solange meine Unit selbst keinen Bug hat und außerhalb des o.g. Sonderfalls richtig funktioniert.
Grüße, Jörg
-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Create and forget: Referenzzählung für Objekte
Das Problem ist halt das es nicht nur der Konstruktor ist, ich weiß von mindestens einem anderen Fall noch, wenn der Record als Enumerator verwendet wird
Das problem ist, es gibt aktuell keinerlei Informationen was den Bug verursacht, es kann sein das der noch bei vielen anderen situationen auftritt, eventuell verschiedenen CPU Targets, optimierungsstufen, etc. es kann sein das der selbe Code bei einem Funktioniert aber nicht bei jemand anderem.
PS: ich hab versucht das mit deinem Record zu reproduzieren, allerdings kam ich nur zu dem ergebnis das dein Code gar nicht freed und nen memory loch überbleibt:
Die StringList wird nie gefreed
Das problem ist, es gibt aktuell keinerlei Informationen was den Bug verursacht, es kann sein das der noch bei vielen anderen situationen auftritt, eventuell verschiedenen CPU Targets, optimierungsstufen, etc. es kann sein das der selbe Code bei einem Funktioniert aber nicht bei jemand anderem.
PS: ich hab versucht das mit deinem Record zu reproduzieren, allerdings kam ich nur zu dem ergebnis das dein Code gar nicht freed und nen memory loch überbleibt:
Code: Alles auswählen
type
TRefCountedSL = specialize TGenericRefCountingRec<TStringList>;
{ TTest }
procedure DoTest;
var
t: TRefCountedSL;
begin
t.Obj := TStringList.Create;
WriteLn(t.Obj.Text);
end;
begin
DOTest;
end.
- Jorg3000
- Lazarusforum e. V.
- Beiträge: 359
- Registriert: So 10. Okt 2021, 10:24
- OS, Lazarus, FPC: Win64
- Wohnort: NRW
Re: Create and forget: Referenzzählung für Objekte
Hm, in meinem Beispielprojekt mit meiner Beispiel-Klasse TMyData wird ganz klar Destroy durchgeführt, wie das ShowMessage('Destroying') in .Destroy anzeigt.
Ich verwende FPC 3.2.0 (Lazarus 2.0.10) auf Windows 10, 64 Bit
PS: Danke für deine Geduld und Mühe!
Ich würde mich freuen, wenn meine Unit so brauchbar wäre, wie ich es heute morgen noch gedacht habe.
Ich verwende FPC 3.2.0 (Lazarus 2.0.10) auf Windows 10, 64 Bit
PS: Danke für deine Geduld und Mühe!
Ich würde mich freuen, wenn meine Unit so brauchbar wäre, wie ich es heute morgen noch gedacht habe.

-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Create and forget: Referenzzählung für Objekte
Es wird seltsam:
Heaptrc:
Also TTest klappt, TStringList nicht. Hab auch in deinen Code gesehen, hab nix falsches auf anhieb gefunden. Außer das du vergessen hast den Management AddRef zu überladen (der wird aufgerufen wenn das objekt als parameter übergeben wird), das kann aber nicht der Grund für diesen Fehler sein
Code: Alles auswählen
TTest = class
destructor Destroy; override;
end;
TRefCountedTest = specialize TGenericRefCountingRec<TTest>;
TRefCountedSL = specialize TGenericRefCountingRec<TStringList>;
destructor TTest.Destroy;
begin
WriteLn('Destroy');
inherited Destroy;
end;
procedure TestTest;
var
t: TRefCountedTest;
begin
t.Obj := TTest.Create;
end;
procedure TestSL;
var
t: TRefCountedSL;
begin
t.Obj := TStringList.Create;
end;
end;
begin
TestTest;
TestSL;
end.
Code: Alles auswählen
Destroy
Heap dump by heaptrc unit of C:\Users\frederic\Desktop\test\test.exe
6 memory blocks allocated : 140/160
4 memory blocks freed : 104/120
2 unfreed memory blocks : 36
True heap size : 294912 (2448 used in System startup)
True free heap : 292208
Should be : 292232
Call trace for block $0152F848 size 12
$00424200 of ReferenceCountingForObjects.pas
$004240D5 of ReferenceCountingForObjects.pas
$00401B06 TESTSL, line 37 of test.pas
$00401B42 main, line 42 of test.pas
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
Call trace for block $0152F7C8 size 24
$004241D0 of ReferenceCountingForObjects.pas
$004240D5 of ReferenceCountingForObjects.pas
$00401B06 TESTSL, line 37 of test.pas
$00401B42 main, line 42 of test.pas
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
$BAADF00D
- Jorg3000
- Lazarusforum e. V.
- Beiträge: 359
- Registriert: So 10. Okt 2021, 10:24
- OS, Lazarus, FPC: Win64
- Wohnort: NRW
Re: Create and forget: Referenzzählung für Objekte
AddRef habe ich bewusst nicht überladen, weil es nach meinem Verständnis keine Auswirkung hat.
Würde ich dabei den Referenzzähler hochzählen, bräuchte ich auch eine Gegenaktion wie SubtractRef o.ä., aber ich glaube das gibt es nicht.
Und dass die StringList nicht freigegeben wird, im Gegensatz zur eigenen Klasse, ist wirklich spannend. Dazu fällt mir gerade nichts ein. Eigentlich sollte ein Destroy ein Destroy sein, egal bei welcher Klasse.
Grüße, Jörg
Würde ich dabei den Referenzzähler hochzählen, bräuchte ich auch eine Gegenaktion wie SubtractRef o.ä., aber ich glaube das gibt es nicht.
Und dass die StringList nicht freigegeben wird, im Gegensatz zur eigenen Klasse, ist wirklich spannend. Dazu fällt mir gerade nichts ein. Eigentlich sollte ein Destroy ein Destroy sein, egal bei welcher Klasse.
Grüße, Jörg
-
- Beiträge: 2118
- Registriert: Di 23. Sep 2014, 17:46
- OS, Lazarus, FPC: Win10 | Linux
- CPU-Target: x86_64
Re: Create and forget: Referenzzählung für Objekte
Zu dem Memoryleak, keine ahnung was das war, grade mal frisches projekt gemacht, exakt selber code, kein memoryleak mehr
Zu AddRef, das gegenstück zu AddRef ist auch Finalize:
Ausgabe:
Zu AddRef, das gegenstück zu AddRef ist auch Finalize:
Code: Alles auswählen
type
{ TTest }
TTest = record
class operator initialize(var t: TTest);
class operator AddRef(var t: TTest);
class operator finalize(var t: TTest);
end;
{ TTest }
class operator TTest.initialize(var t: TTest);
begin
writeLn('init');
end;
class operator TTest.AddRef(var t: TTest);
begin
writeLn('addref');
end;
class operator TTest.finalize(var t: TTest);
begin
writeLn('final');
end;
procedure Test2(A: TTest);
begin
end;
procedure Test;
var
t: TTEst;
begin
Test2(t);
WriteLn('Ende Test');
end;
Code: Alles auswählen
init
addref
final
Ende Test
final