Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Speicherfreigabe mit Dispose (https://www.delphipraxis.net/194586-speicherfreigabe-mit-dispose.html)

JRadke 9. Dez 2017 17:21

Speicherfreigabe mit Dispose
 
Ich habe ein Verständnisproblem bei der Speicherfreigabe mit Dispose.
Ich habe mir einen Ringspeicher zur Aufnahme von Messdaten auf Basis eines TList definiert.
Vor dem Hinzufügen eines neuen Elements wird zunächst geprüft, ob die Maximalgröße des Buffers erreicht ist und gegebenenfalls das älteste Element (also das erste in der Liste gelöscht).
Im Code sieht das etwa so aus:

Code:
Buffer: TList;

procedure AddRecord(ARecord: TDataRecord);
var
  P: PDataRecord;
begin
  New(P);
  P^.Values := Copy(ARecord.Values);
  if Buffer.Count = fMaxBufSize then begin
    Dispose(Buffer.Items[0]);
    Buffer.Delete(0);
  end;
  Buffer.Add(P);
end;
Hierbei wird aber durch Dispose der Speicherplatz des Elements Buffer.Items[0] nicht freigegeben!
Erst wenn ich den Code wie folgt umschreibe, funktioniert es:

Code:
procedure AddRecord(ARecord: TDataRecord);
var
  P, P2: PDataRecord;
begin
  New(P);
  P^.Values := Copy(ARecord.Values);
  if Buffer.Count = fMaxBufSize then begin
    P2 := Buffer.Items[0];
    Dispose(P2);
    Buffer.Delete(0);
  end;
  Buffer.Add(P);
end;
Kann mir jemand den Unterschied erklären?
Das Problem führte bei mir nach einigen Wochen Programmlaufzeit zu einem Speicherfehler und war daher sehr schwer zu lokalisieren.

jbg 9. Dez 2017 18:02

AW: Speicherfreigabe mit Dispose
 
TList.Items[] liefert einen untypisierten Zeiger (Datentyp Pointer). Dispose weiß somit nicht, dass sich hinter dem Zeiger ein PDataRecord befindet und wird zu einem einfachen "FreeMem" umfunktioniert, womit alle "managed" Datentypen innerhalb des Records nicht richtig aufgeräumt werden.

Beim zweiten Beispiel weiß Dispose nun vom passenden Typ und kann den Inhalt des Records (Strings, Dynamische Arrays, Interfaces) korrekt aufräumen.

himitsu 9. Dez 2017 18:20

AW: Speicherfreigabe mit Dispose
 
Wäre es nicht besser, wenn Dispose hier eine Warnung ausgeben würde, wenn es einen untypisierten Pointer bekommt?

jbg 9. Dez 2017 18:25

AW: Speicherfreigabe mit Dispose
 
Zitat:

Zitat von himitsu (Beitrag 1388391)
Wäre es nicht besser

Sicher, für untypisierte Zeiger kann man auch direkt FreeMem schreiben, aber das war schon zu TurboPASCAL Zeiten so und wird wohl auch nie geändert.

JRadke 9. Dez 2017 19:51

AW: Speicherfreigabe mit Dispose
 
Danke für die Antworten.
Die Erklärung von jbg ist einleuchtend, hier heißt es also aufzupassen, was für einen Zeiger man übergibt.

freimatz 11. Dez 2017 15:51

AW: Speicherfreigabe mit Dispose
 
Zitat:

Zitat von himitsu (Beitrag 1388391)
Wäre es nicht besser, wenn Dispose hier eine Warnung ausgeben würde, wenn es einen untypisierten Pointer bekommt?

Es wäre besser Dispose gar nicht zu verwenden. :wink:

Zacherl 11. Dez 2017 21:11

AW: Speicherfreigabe mit Dispose
 
Zitat:

Zitat von freimatz (Beitrag 1388505)
Zitat:

Zitat von himitsu (Beitrag 1388391)
Wäre es nicht besser, wenn Dispose hier eine Warnung ausgeben würde, wenn es einen untypisierten Pointer bekommt?

Es wäre besser Dispose gar nicht zu verwenden. :wink:

Manchmal kommt man nicht drum rum.

In diesem konkreten Falle allerdings durchaus. Dieser "Ringspeicher" ist nämlich alles andere als ein tatsächlicher Ringspeicher und
Delphi-Quellcode:
TList
zu verwenden ist von der Performance her unter Umständen ziemlich übel. Das
Delphi-Quellcode:
Delete(IrgendwasMittendrin)
kostet hier nämlich sehr viel Zeit.

Alternative wäre es ein
Delphi-Quellcode:
TArray<T>
oder ggfls.
Delphi-Quellcode:
array[n..m] of T
zu verwenden und sich dazu den Start- und den End-Index zu merken. Add/Remove aus dem Buffer beschränkt sich dann auf ein einfaches Einfügen der Daten mit abschließendem
Delphi-Quellcode:
Inc()
bzw.
Delphi-Quellcode:
Dec()
der entsprechenden Indexvariable. Dynamische Speicherreservierungen hättest du dann zur Laufzeit auch komplett keine mehr.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:56 Uhr.

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