Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi SetLength(dyn. array, 0) vs. Finalize() vs. nil (https://www.delphipraxis.net/56393-setlength-dyn-array-0-vs-finalize-vs-nil.html)

Nogge 4. Nov 2005 22:45


SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Hallo Community,
Ich habe mal im Forum rechergiert, welche Möglichkeiten es gibt, dyn. arrays freizugeben. Dabei bin ich auf die o.g. Prozeduren bzw. nil gestoßen und bin jetzt leicht verwirrt. Ich zitiere einfach mal die verschiedenen Aussagen:
Zitat:

Zitat von Muetze1
Ich hatte früher öfters irgendwo und sporadisch Speicherlecks und z.T. Zugriffsverletzungen ohne Grund - bis ich diese beiden Funktionen eingesetzt habe. Zum grundlegenden Einsatz:

- Vor der Verwendung des Arrays einmalig Initialize() (also auch vor dem SetLength())
- Nach der Verwendung des Arrays einmalig Finalize() (also zuvor SetLength(arr, 0))

Zitat:

Zitat von sakura
Delphi-Quellcode:
SetLength(MyFlexibleArray, 20);
allocates an array of 20 reals, indexed 0 to 19. Dynamic arrays are always integer-indexed, always starting from 0.
Dynamic-array variables are implicitly pointers and are managed by the same reference-counting technique used for long strings. To deallocate a dynamic array, assign nil to a variable that references the array or pass the variable to Finalize; either of these methods disposes of the array, provided there are no other references to it. Dynamic arrays of length 0 have the value nil. Do not apply the dereference operator (^) to a dynamic-array variable or pass it to the New or Dispose procedure.

Zitat:

Zitat von Chewie
Sie bewirken das gleiche, wobei die nil-Variante ein paar Takte schneller ist. Bei der Zuweisung an nil wird gleich DynArrayClear aufgerufen, bei der Variante mit SetLength wird erst DynArraySetLength aufgerufen, welches dann wiederum besagtes DynArrayClear aufruft.

Nach dem ganzen Hin- und Her: Nehmen wir mal zwei Beispiele:
Delphi-Quellcode:
type
  TStringArray = array of String;
var
  Partition : array of TTreeNode;
  Dirs      : TStringArray;
  [...]
  SetLength(Partition,10);
  SetLength(Dirs,3);
  [...]
  // Freigeben beider dyn. arrays
Wie sollte ich jetzt die beiden dyn. arrays sauber freigeben, sodass der gesamte Speicherbereich wieder an Windows übergeben wird?
Btw: Wie müsste ich ein array of "record" korrekt freigeben, wenn dieser u.a. Short- bzw. LongStrings (= unbegrenzte Angabe), Integers und TreeNodes enthalten würde?

Muetze1 5. Nov 2005 14:29

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Zitat:

Zitat von Nogge
..., sodass der gesamte Speicherbereich wieder an Windows übergeben wird?

Nur eine kurze Anmerkung zu dieser Frage und nicht zum Problem an sich: Der Speicherbereich wird nicht an Windows sondern an den Heap zurück gegeben. Ob der Heap sich verkleinert und Teile an Windows zurück gibt, das hängt sehr stark vom verwendeten Speichermanager ab - die meisten verkleinern sich nicht mehr (schon als verwaltungstechnischen Gründen).

Zum Thema:

Ich kann zu meiner vorherigen Aussage trotzdem nur nochmals ausführen, was ich festgestellt habe:

1. Dynamischen Arrays einfach NIL zuweisen gibt diese nicht sauber frei und werden als verlorener Speicher bei Speichertestern ausgegeben (Delphi 4 und Delphi 5)
2. lokale dynamische Arrays in Proceduren hinterlassen !sporadisch! Speicherlecks, wenn nach dem SetLength() kein Finalize() kommt (Delphi 4)
3. direktes anwenden von Finalize auf ein dynamisches Array führt zu einer ungenügenden Array Freigabe, es bleiben im Schnitt 8 Bytes nicht freigegeben zurück (Delphi 5).

Bei höheren Delphiversionen mag das alles klappen und ein vernünftiger Weg sein.

Nogge 5. Nov 2005 15:18

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Also nach deiner Auffassung sollte ich bei dem Beispiel die dyn. arrays so freigeben?
Delphi-Quellcode:
SetLength(Partition,0);
SetLength(Dirs,0);
Finalize(Partition);
Finalize(Dirs);
Muss ich nicht die einzelnen TreeNodes aus dem array "Partition" seperat freigeben oder übernimmt gerade Finalize() diese Aufgabe für mich?

marabu 5. Nov 2005 15:45

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Ein dynamisches Array mit dem base type TTreeNode ist recht ungewöhnlich. Grundsätzlich lebt ein TTreeNode in der Obhut von TTreeNodes und dieser Container verwaltet die Nodes von der Wiege bis zur Bahre. Finalize(), angewandt auf das dynamische array, weiß nichts von diesem Doppelleben der Nodes und darf auch gar nichts darüber wissen.

Grüße vom marabu

Nogge 5. Nov 2005 16:21

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Jo, deine Aussage war richtig. Es ist wirklich ungewöhnlich und sinnlos, solch ein array zu deklarieren, wie ich in meinem Code bemerkt habe. Habe das jetzt korrigiert.
Zu Finalize(): Diese Prozedur wird also nur auf Variablen mit undefinierbarer Länge angewandt, wie z.B. auf long-strings, variants usw.? D.h. es gibt keinen Grund, diese Prozedur auf array of Extended oder array of String[200] bzw. Shortstring loszulassen, richtig?
Noch mal zu records:
Delphi-Quellcode:
type
  PGroupData = ^TGroupData;
  TGroupData = record
    GroupID     : Word;
    ShouldExpand : Boolean;
    S           : String;
  end;
var
  TestArrayT : array of TGroupData; // wird eher selten benutzt, nicht wahr?
  TestArrayP : array of PGroupData;
Da in diesem record eine Variable mit undefinierter Länge (= String) enthalten ist, müsste ich diesen doch beim Freigeben des arrays mit Finalize() leeren, oder? Und wenn ja, wie genau?
Wenn ich jetzt falsch liege, erklärt mir bitte das Gegenteil mithilfe der Beipiele. Das hilft mir ungemein.

marabu 5. Nov 2005 16:56

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Zitat:

Zitat von Nogge
Zu Finalize(): Diese Prozedur wird also nur auf Variablen mit undefinierbarer Länge angewandt, wie z.B. auf long-strings, variants usw.?

Ich würde nicht von undefinierbarer Länge sprechen, sondern von speziellen dynamischen Variablen. In Delphi werden dynamische Variablen normalerweise mit New() und Dispose() verwaltet. Mit den long strings, den variants und den interfaces (die Aufzählung ist nicht vollständig) hat eine neue Klasse "dynamischer Variablen" in Object Pascal Einzug gehalten, bei der auch oft von compiler magic gesprochen wird. Es sind dynamische Variablen, bei denen nicht du es bist, der New() und Dispose() aufruft. Das Laufzeitsystem weiß, wann es Speicher braucht und wann es ihn wieder freigeben muss - wenn du ihm nicht in die Quere kommst.

Zitat:

Zitat von Nogge
D.h. es gibt keinen Grund, diese Prozedur auf array of Extended oder array of String[200] bzw. Shortstring loszulassen, richtig?

Falsch, aber das müsstest du selbst erkennen, wenn du meinen Beitrag bis hierhin gelesen hast. Dynamische Arrays gehören halt auch zu dieser speziellen Variablen-Klasse.

Delphi-Quellcode:
type
  PGroupData = ^TGroupData;
  TGroupData = record
    GroupID     : Word;
    ShouldExpand : Boolean;
    S           : String;
  end;
var
  TestArrayT : array of TGroupData; // wird eher selten benutzt, nicht wahr?
  TestArrayP : array of PGroupData;
Zitat:

Zitat von Nogge
Da in diesem record eine Variable mit undefinierter Länge (= String) enthalten ist, müsste ich diesen doch beim Freigeben des arrays mit Finalize() leeren, oder? Und wenn ja, wie genau?

Finalize(TestArrayT, Length(TestArrayT)) sorgt dafür, dass für alle Elemente deines Arrays die reference counts dekrementiert werden können und der garbage collector seines Amtes walten kann.

Für deinen zweiten Array-Typ bin ich im Augenblick überfragt. Ich vermute sehr stark, dass keine Finalisierung statt finden wird, aber ich müsste es untersuchen um sicher zu sein.

marabu

Nogge 5. Nov 2005 19:33

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Vielen Dank für die detailierten und klärenden Erläuterungen, Muetze1 und marabu.
Zusammenfassend kann man also sagen:
Mit SetLength initialisierte dyn. arrays sollten mit (in der Reihenfolge)
1.) SetLength(array, 0);
und
2a.) Finalize(array);
bzw.
2b.) Finalize(array, Length(array));
freigegeben werden.
2a) wird benutzt, wenn ein array of vordefinierte Typen (= string, Integer, Real, Boolean usw.) verwendet wird.
2b) wird benutzt, wenn ein array of TRecord verwendet wird.

Erbitte Bestätigung für diese Zusammenfassung. Danke.

marabu 5. Nov 2005 19:52

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Ich sehe keinen Interpretationsspielraum in dieser Frage. "Dynamische Typen" sind die internen Typen LongString, WideString, Variant, Array, Record, Interface und DynArray. Wenn v eine Variable ist und ihr Typ zu den zuvor genannten zählt, dann ist Finalize(v) bzw. Finalize(v, Length(v)) angesagt. Bei Arrays folgt dann SetLength(v, 0), bei den anderen Typen v := nil - so ist man auf der sicheren Seite.

marabu

Nogge 5. Nov 2005 23:08

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Ok, ok...
eins noch: Gerade an dieser Stelle kreuzen sich 2 Meinungen:
Zitat:

Zitat von marabu
Wenn v eine Variable ist und ihr Typ zu den zuvor genannten zählt, dann ist Finalize(v) bzw. Finalize(v, Length(v)) angesagt. Bei Arrays folgt dann SetLength(v, 0).

Zitat:

Zitat von Muetze1
2. lokale dynamische Arrays in Proceduren hinterlassen !sporadisch! Speicherlecks, wenn nach dem SetLength() kein Finalize() kommt (Delphi 4).

Sry, wenn ich dich vllt nerve, aber kommt Finalize() jetzt hinter oder vor SetLength()?

marabu 6. Nov 2005 07:46

Re: SetLength(dyn. array, 0) vs. Finalize() vs. nil
 
Hallo Nogge,

wenn SetLength(v, 0) zuerst ausgeführt wird, dann kommt Finalize(v, Length(v)) doch nach der Feier.

Delphi-Quellcode:
type
  TItem = packed record
    Name: string;
    Amount: integer;
  end;

var
  Items: array of TItem;
  i: integer;

begin
  // es werden 3 mal 8 Byte vom heap besorgt
  SetLength(Items, 3);

  // die einzelnen Items werden initialisert
  for i := Low(Items) to High(Items) do
    with Items[i] do
    begin
      // der pointer "Name" wird auf einen dynamischen
      // Speicherblock gesetzt, der 6 Byte Nutzdaten zzgl.
      // Verwaltungsinformationen (Längenzähler, term char, etc.
      // umfasst.
      Name := 'Name-' + IntToStr(Succ(i));
      // nativer Datentyp, kein heap beteiligt
      Amount := Succ(Random(5));
    end;
  end;

  // Alle Zeiger "Name" werden auf nil gesetzt
  // der garbage collector beobachtet den auf
  // 0 gehenden refence count und deallokiert
  // den von den strings auf dem heap belegten Speicher
  Finalize(Items, Length(Items));

  // Die 3 * 8 Byte müssen auch noch an den heap
  // zurückgegeben werden.
  SetLength(Items, 0);

end;
Sonntagsgrüße vom marabu


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:44 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz