Discussion:
Dynamische Arrays mit TList verwalten? (D6)
(zu alt für eine Antwort)
Gottfried Helms
2015-06-22 14:48:56 UTC
Permalink
Hi -

gerade versuche ich in meinem Matrix-Programm Funktionen
zu bauen, die einen Satz verschiedner Matrizen (z.B. drei
Matrizen mit Real-Datentyp, 2 mit Stringdatentyp) ausgeben
können.
Nachdem das alles mit dynamischen Arrays selbst gut läuft,
hatte ich sogar als Rückgabewert einer Funktion einen
(dynamischen) Array von (dynamischen) Real-matrizen imple-
mentiert. Alles läuft gut.

Jetzt möchte ich als Funktionsrückgabe mehr Flexibilität
und eben wie oben genannt, gemischt Real-/String- evtl
Boolean- etc Matrizen verwalten können, und dachte ich
kann das vielleicht mit TList implementieren.

Zunächst schien das auch zu laufen, aber ein Speicher-test
(via taskmgr) ergab, daß zwischen

function meinemathematischefunktion(a.b): tmylist;
begin
setlength(myrealmatrix,a,a);
setlength(mystringmatrix,b,b);

result := tmylist.create;
with result do begin
mylist.add(myrealmatrix) ;
mylist.add(mystringmatrix) ;
... etc ...
end;
end; // ende der Funktion
und

for k:=1 to 50 do begin
resultlist:=meinemathematischefunktion(20*k,30*k);
dosomething(resultlist.items[0]);
dosomethingelse(resultlist.items[1]);
...
resultlist.deleteall ;
resultlist.destroy ;

die erzeugten Matrizen nicht alle gelöscht/freigegeben
werden obwohl ich in deleteall genau darauf geachtet
habe was ich da mache.

Wenn *überhaupt* keine Speicherfreigabe passieren würde,
wüßte ich ja, daß ich irgendwas grundsätzlich falsch
mache - das ist also offensichtlich nicht ganz der Fall.
Aber das gerade *die Hälfte* des nach -z.b. in einer
Testroutine - 50 fachen add von 200x200 Matrizen
allokierten Speichers freigeben wird wenn ich deleteall
und destroy mache, ist irgendwie komisch.

Gibt's jemand der mit diesem Problem Erfahrung hat?
Ich vermute mal, daß irgendwo ein kleiner Wurm in meinem
Verständnis steckt, und ich das Problem lösen kann, wenn
ich den notwendigen Trick sehe.

Vielleicht hat jemand sowas gerade parat, wie man
so eine Klasse tmylist implementieren müßte?
ALso es scheint ja wohl zu sein daß an irgendeiner
Stelle statt einer zwei Matrizen erzeugt werden, wobei
dabei aber eine der beiden Adressen verlorengeht und
deshalb beim deleteall nicht angesprochen werden kann?
(jedenfalls meine aktuelle Hypothese...)

Gottfried
Hans-Peter Diettrich
2015-06-22 16:32:04 UTC
Permalink
Post by Gottfried Helms
Jetzt möchte ich als Funktionsrückgabe mehr Flexibilität
und eben wie oben genannt, gemischt Real-/String- evtl
Boolean- etc Matrizen verwalten können, und dachte ich
kann das vielleicht mit TList implementieren.
TList verwaltet nur Pointer, d.h. bei Strings und dynamischen Arrays
werden die Referenzen nicht gezählt, und das führt dann zu Speicherlecks.
Post by Gottfried Helms
Zunächst schien das auch zu laufen, aber ein Speicher-test
(via taskmgr) ergab, daß zwischen
function meinemathematischefunktion(a.b): tmylist;
begin
setlength(myrealmatrix,a,a);
setlength(mystringmatrix,b,b);
result := tmylist.create;
Da wäre auch sowas wie SetLength (Capacity?) nett, sonst wird die Liste
dynamisch erweitert, und dabei gehen die Referenzen (Zähler) kaputt.
Post by Gottfried Helms
Wenn *überhaupt* keine Speicherfreigabe passieren würde,
wüßte ich ja, daß ich irgendwas grundsätzlich falsch
mache - das ist also offensichtlich nicht ganz der Fall.
Aber das gerade *die Hälfte* des nach -z.b. in einer
Testroutine - 50 fachen add von 200x200 Matrizen
allokierten Speichers freigeben wird wenn ich deleteall
und destroy mache, ist irgendwie komisch.
Die ungelöschte Hälfte dürften die Zwischenergebnisse sein, die nach dem
Verlängern der Liste noch im Speicher liegen.
Post by Gottfried Helms
Vielleicht hat jemand sowas gerade parat, wie man
so eine Klasse tmylist implementieren müßte?
Schau mal in TStringList nach, wie dort die Änderungen von Capacity und
das Einfügen/Löschen von Elementen implementiert sind. Falls möglich -
bei Matrizen sind die Dimensionen ja bekannt - die Arrays gleich im
Konstruktor richtig dimensionieren, und auf Anhängen/Einfügen/Löschen
ganz verzichten. Initialisierung etc. nur über Index (Get/Set) im Rahmen
von Capacity erlauben.

Ansonsten mußt Du selbst wissen, was Deine Matrizen enthalten
können/sollen. Eine erweiterte TStringList könnte neben einem String und
einem TObject (Pointer) noch weitere Elemente enthalten, oder die
zusätzlichen Elemente in einem TObject (Objects[i].RealVal usw.) ablegen.

DoDi
Gottfried Helms
2015-06-22 19:27:58 UTC
Permalink
Post by Hans-Peter Diettrich
Die ungelöschte Hälfte dürften die Zwischenergebnisse sein, die nach dem
Verlängern der Liste noch im Speicher liegen.
Das klingt irgendwie plausibel, möglich. Vielleicht finde
ich das was in einer geeigneten Testumgebung.
Post by Hans-Peter Diettrich
Post by Gottfried Helms
Vielleicht hat jemand sowas gerade parat, wie man
so eine Klasse tmylist implementieren müßte?
Schau mal in TStringList nach, wie dort die Änderungen von Capacity und
das Einfügen/Löschen von Elementen implementiert sind. Falls möglich -
bei Matrizen sind die Dimensionen ja bekannt - die Arrays gleich im
Konstruktor richtig dimensionieren, und auf Anhängen/Einfügen/Löschen
ganz verzichten. Initialisierung etc. nur über Index (Get/Set) im Rahmen
von Capacity erlauben.
Ich sehe mir das mal genauer an, vielleicht extrahiere
ich mal die Teile die ich brauche und nenne das Ganze
dann tmatrixlist wenn es funktioniert. Das was bei
tstringlist die "objects" sind, kann ich dann vielleicht
in einen laufzeit-typ-deskriptor umbauen.

Schonmal vielen Dank für die Tips!

Gottfried
Peter Below (TeamB)
2015-06-22 17:23:01 UTC
Permalink
Post by Gottfried Helms
Hi -
gerade versuche ich in meinem Matrix-Programm Funktionen
zu bauen, die einen Satz verschiedner Matrizen (z.B. drei
Matrizen mit Real-Datentyp, 2 mit Stringdatentyp) ausgeben
können.
Nachdem das alles mit dynamischen Arrays selbst gut läuft,
hatte ich sogar als Rückgabewert einer Funktion einen
(dynamischen) Array von (dynamischen) Real-matrizen imple-
mentiert. Alles läuft gut.
Jetzt möchte ich als Funktionsrückgabe mehr Flexibilität
und eben wie oben genannt, gemischt Real-/String- evtl
Boolean- etc Matrizen verwalten können, und dachte ich
kann das vielleicht mit TList implementieren.
Das ist nicht so einfach, fürchte ich. Dynamische arrays unterliegen
wie String einer automatischen Speicherverwaltung durch den Compiler.
Wenn eine solche Variable aus dem Scope geht wird der Reference count
runtergezählt und dann eventuell der Speicher für den Array freigegeben.

Eine solchen Array in einer TList abzulegen ist zwar formal möglich
(die Variable enthält ja nur einen Pointer), aber damit unterläuft man
die automatische Speicherverwaltung und das bedeutet normalerweise jede
Menge Ärger.

Bau Dir lieber einen Satz Klassen, die intern die Daten in einem
dynamic array lagern, und reiche dann Objektreferenzen herum. Damit
hast Du die Speicherverwaltung unter eigener Kontrolle.

Leider ist D6 ja uralt und unterstützt keine generics, die würden sowas
deutlich einfacher machen...

Nochwas: die Angaben zur Speichernutzung in Task Manager sind
irreführend wenn es um den Delphi memory manager geht. Der holt Memory
in großen Happen vom OS, teil sie nach Anforderung auf, und gibt keinen
Block zurück, solange auch nur noch ein einziges Byte in dem Block noch
als belegt markiert ist.
--
Peter Below (TeamB)
Gottfried Helms
2015-06-22 19:42:23 UTC
Permalink
(...)
Post by Peter Below (TeamB)
Das ist nicht so einfach, fürchte ich. Dynamische arrays unterliegen
wie String einer automatischen Speicherverwaltung durch den Compiler.
Wenn eine solche Variable aus dem Scope geht wird der Reference count
runtergezählt und dann eventuell der Speicher für den Array freigegeben.
Tja, habe ich auch dran gedacht; aber meine Maßnahmen scheinen
noch nicht auszureichen...
Post by Peter Below (TeamB)
Eine solchen Array in einer TList abzulegen ist zwar formal möglich
(die Variable enthält ja nur einen Pointer), aber damit unterläuft man
die automatische Speicherverwaltung und das bedeutet normalerweise jede
Menge Ärger.
Bau Dir lieber einen Satz Klassen, die intern die Daten in einem
dynamic array lagern, und reiche dann Objektreferenzen herum. Damit
hast Du die Speicherverwaltung unter eigener Kontrolle.
Hmm, 60.000+ Zeilen Code, und meine tmatrix, tstringmatrix etc
sind die zentralen Datentypen. Also ... wenn sowas mit tlist/tstringlist
und dem Aufbohren nicht gehen sollte, muß ich das wohl eher lassen.
Oder
Post by Peter Below (TeamB)
Leider ist D6 ja uralt und unterstützt keine generics, die würden sowas
deutlich einfacher machen...
Nochwas: die Angaben zur Speichernutzung in Task Manager sind
irreführend wenn es um den Delphi memory manager geht. Der holt Memory
in großen Happen vom OS, teil sie nach Anforderung auf, und gibt keinen
Block zurück, solange auch nur noch ein einziges Byte in dem Block noch
als belegt markiert ist.
Ok, das könnte natürlich auch der Grund sein; ich habe bei der Listenverwaltung
in einer kleinen Testumgebung in der ich die Matrizengrößen 0 setze immer
noch kleine Leaks, also möglicherweise einfach die array-Verwaltungs-
strukturen. Und die könnten bei den großen matrizen (probehalber auf
jeweils auf 1 MB gesetzt damit der Taskmgr das mitbekommt) der wirkliche
Grund für die SPeicherleaks sein und die Pointerverwaltung wäre möglicherweise
in Ordnung...

Mal sehen.

Erst mal vielen Dank, ich kann das leider erst am Do/Fr weiterverfolgen...

Ich erinnere irgendwas dunkel, daß in den 2000-2005 er Jahren, ich
glaube Benedikt Mangelsdorff, mal geschrieben hat, er würde für
tlisten von irgendwelchen Datentypen sich immer eigene komplette
Basis-tlisten neu ableiten. Aber ich weiß nicht ob er hier auch mal
Beispielcode gezeigt hatte...

hmmm....

Gottfried
Hans-Peter Diettrich
2015-06-23 07:48:31 UTC
Permalink
Post by Gottfried Helms
Ok, das könnte natürlich auch der Grund sein; ich habe bei der Listenverwaltung
in einer kleinen Testumgebung in der ich die Matrizengrößen 0 setze immer
noch kleine Leaks, also möglicherweise einfach die array-Verwaltungs-
strukturen. Und die könnten bei den großen matrizen (probehalber auf
jeweils auf 1 MB gesetzt damit der Taskmgr das mitbekommt) der wirkliche
Grund für die SPeicherleaks sein und die Pointerverwaltung wäre möglicherweise
in Ordnung...
Zum Auffinden und Beheben von Speicherlecks fand ich FastMM4 ganz
hilfreich. Aber aufpassen, die neueren Versionen compilieren nicht mehr
mit den alten Delphi-Versionen. Nimm also eine alte Version, die Updates
für D20xx brauchst Du ja nicht.

DoDi

Loading...