Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   record fillchar Speicherleck (https://www.delphipraxis.net/180020-record-fillchar-speicherleck.html)

baumina 17. Apr 2014 08:09

record fillchar Speicherleck
 
Speicherverwaltung war noch nie meine Stärke. Wie vermeide ich sinnvoll folgendes Speicherleck?

Delphi-Quellcode:
program Project2;

uses
  Forms;

{$R *.res}

type
  MyRecord = record
    S1 : String;
    S2 : String;
  end;

var
  GlobVar : MyRecord;

begin
  ReportMemoryLeaksOnShutdown := True;
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  fillchar(GlobVar, SizeOf(GlobVar), 0);
  Globvar.S1 := 'testString';
  fillchar(GlobVar, SizeOf(GlobVar), 0); <-- ab jetzt Speicherleck
  Application.Run;
end.
Hintergrund : Ich hatte mich für einen globalen Record entschieden, da dieser alle wichtigen Daten meines aktuell gewählten Mantanten enthält, auf die ich ständig zugreifen muss. Fillchar deswegen, um bei einem Mandantenwechsel erst alle Inhalte zu leeren.

Bernhard Geyer 17. Apr 2014 08:14

AW: record fillchar Speicherleck
 
Mit Fillchar zerstörst du die Verwaltungsinformationen der Strings (Referenzzählung etc.) Also ganz böser Fehler!

Du solltest nur

Delphi-Quellcode:
GlobVar.S1 := ''
GlobVar.S2 := ''
machen

Nersgatt 17. Apr 2014 08:21

AW: record fillchar Speicherleck
 
Ich würde dem Record dann eine Procedure verpassen, um ihn zu leeren:

Delphi-Quellcode:
MyRecord = record
    S1 : String;
    S2 : String;
    procedure Clear;
  end;
Somit könntest Du dann irgendwo einfach GlobVar.Clear aufrufen.

baumina 17. Apr 2014 08:32

AW: record fillchar Speicherleck
 
Klasse, danke Euch beiden!!!!

Stevie 17. Apr 2014 08:39

AW: record fillchar Speicherleck
 
Oder
Delphi-Quellcode:
GlobVar := Default(MyRecord);
Abgesehen davon werden globale Variablen sowieso initialisiert, d.h. es besteht keine Veranlassung in deinem Beispiel, das von Hand zu tun (der Aufruf vor dem Zuweisen des strings).
Anders sähe es natürlich bei einer lokalen Variable aus!

baumina 17. Apr 2014 09:03

AW: record fillchar Speicherleck
 
Delphi-Quellcode:
Default
Toller Befehl! Kenn ich nicht, finde auch keine Hilfe dazu, aber funktioniert, mal schauen ob ich mich daran in zwei Jahren noch erinnern kann. :thumb:

himitsu 17. Apr 2014 09:05

AW: record fillchar Speicherleck
 
Und Delphi-Referenz durchsuchenFinalize da, wo es
Delphi-Quellcode:
Default(...)
noch nicht gab.

Aber das ganze in die Clear, Metode auszulagen kann nicht schaden. (wie dann darin geleert wird, ist egal, Hauptsache es wird "richtig" gemacht)



FillChar setzt "blind" alles auf 0, wobei natürlich mutwillig die automatisch verwalteten Referenzen geschrottet werden.

p80286 17. Apr 2014 10:00

AW: record fillchar Speicherleck
 
Zitat:

Zitat von himitsu (Beitrag 1255953)
Und Delphi-Referenz durchsuchenFinalize da, wo es
Delphi-Quellcode:
Default(...)
noch nicht gab.

Gute Idee. Aber, ich hab gerade in der Hilfe gestöbert, was ist mit solchen Records:
Delphi-Quellcode:
record
  Name : string;
  Vorname : string;
  Geburtstag : tdatetime;
  Kenner : array [0..8] of char;
  GenommeneUrlaubstage : byte;
end;
werden Geburtstag,Kenner und GenommeneUrlaubstage genullt oder nicht?

Gruß
K-H

himitsu 17. Apr 2014 11:57

AW: record fillchar Speicherleck
 
Kommt jetzt drauf an, wie es implementiert wurde.

Bei "Optimaler Laufzeigt" würden diese nicht behandelt,
aber es spricht nichts dagegen nach dem Finalize nochmal ein FillChar/ZeroMemory drüber laufen zu lassen. (nil bleibt dann ja 0)

Interfaces, dynamische Arrays (Kenner ist statisch), LongStrings (also AnsiString, WideString und UnicodeString) und Variants würde man sonst schrotten und Speicherlecks erschaffen.
Wobei ShortString (und die Typen String[x]) aber ein statisches Array ist.

Stevie 17. Apr 2014 13:14

AW: record fillchar Speicherleck
 
Zitat:

Zitat von p80286 (Beitrag 1255973)
Gute Idee. Aber, ich hab gerade in der Hilfe gestöbert, was ist mit solchen Records:
Delphi-Quellcode:
record
  Name : string;
  Vorname : string;
  Geburtstag : tdatetime;
  Kenner : array [0..8] of char;
  GenommeneUrlaubstage : byte;
end;
werden Geburtstag,Kenner und GenommeneUrlaubstage genullt oder nicht?

Ja, Default macht nix anderes, als zusätzlich noch FinalizeRecord aufzurufen, wenn managed Fields vorhanden sind.

Hier mal der Auszug aus dem assembler code:
Code:
006494C5 8D45D0           lea eax,[ebp-$30]
006494C8 8B15E8936400     mov edx,[$006493e8]
006494CE E809EEDBFF      call @FinalizeRecord
006494D3 8BF8             mov edi,eax
006494D5 33C0             xor eax,eax
006494D7 B90A000000       mov ecx,$0000000a
006494DC F3AB            rep stosd
Die Funktion gibts seit Generics, damit man in generischem Code ein leeres Element von irgendeinem Typ erzeugen kann (Default(T)).
Lässt sich aber ebend auch prima in nicht generischem Code benutzen.

sx2008 19. Apr 2014 06:23

AW: record fillchar Speicherleck
 
Zitat:

Zitat von baumina (Beitrag 1255936)
Ich hatte mich für einen globalen Record entschieden, da dieser alle wichtigen Daten meines aktuell gewählten Mantanten enthält

Hat denn dein Mandant keine Methoden wie z.B. Load(), Save()?
Oder vielleicht brauchst du irgendwann eine Methode die Länderkennzeichen, Postleitzahl und Ort in einem String liefert.
Warum also mit einem Record beginnen wenn doch eine Klasse einen Mehrwert bietet?

Und du kannst mir glauben - irgendwann kommst du an den Punkt an dem du merkst dass du mehrere Mandanten-Objekte brauchst.
Dann wirst du ärgern dass du an tausend Stellen auf einen globalen Record zugreifst.

Dejan Vu 19. Apr 2014 07:28

AW: record fillchar Speicherleck
 
[rethorische Frage]Wozu überhaupt noch Records?[/rethorische Frage]

himitsu 19. Apr 2014 09:44

AW: record fillchar Speicherleck
 
Records kann man in einem Zug in und aus einem Stream kopieren. (allerdings dürfen dann keine Pointer-Typen enthalten sein)

Records haben eine automatische Speicherverwaltung

Man zieht nicht einen ganz so großen Schwanz an Vererbungs unr RTTI-Zeugs mit.

Und man kann sowas wie die Operatoren nutzen, welches ohne eine automatische Speicherverwalung nunmal nicht geht. (nja, bei Interfaces und bei Objekten mit ARC ginge es dennoch, wenn/falls es implementiert ist und wenn die endlich mal den seit Jahren von mir gewünschen Copy-Operator implementieren)

Sir Rufo 19. Apr 2014 10:04

AW: record fillchar Speicherleck
 
Generell ist das Verwenden von globalen Variablen zu vermeiden, vor allem, weil es eine einfache Möglichkeit gibt, diese zu schützen.
Delphi-Quellcode:
interface

function GlobValue : MyRecord;
procedure SetGlobValue( const Value : MyRecord );

implementation

var
  _GlobValue;

function GlobValue : MyRecord;
begin
  Result := _GlobValue;
end;

procedure SetGlobValue( const Value : MyRecord );
begin
  // prüfen, ob die Änderung erlaubt ist
  _GlobValue := Value;
  // Benachrichtigung über die Änderung
end;
Sinnvoll ist es auch den Record immutable (unveränderlich) zu gestalten und die Erzeugung des Wertes über einen Konstruktor zu regeln:
Delphi-Quellcode:
MyRecord = record
private
  FS1 : string;
  FS2 : string;
public
  constructor Create( const S1, S2 : string );
  property S1 : string read FS1;
  property S2 : string read FS2;
end;

constructor MyRecord.Create( const S1, S2 : string );
begin
  // Validierung
  FS1 := S1;
  FS2 := S2;
end;
Im Konstruktor kann jetzt auf Plausibilität geprüft werden und bei unsinnigen Werten wirft man eine Exception.

Mit diesen einfachen Mitteln wird es unmöglich gemacht, dass unbemerkt Änderungen gemacht werden oder inkonsistente Daten vorliegen.

Bjoerk 19. Apr 2014 12:42

AW: record fillchar Speicherleck
 
Zitat:

Zitat von Dejan Vu (Beitrag 1256212)
[rethorische Frage]Wozu überhaupt noch Records?[/rethorische Frage]

Records eignen sich gut als Result von functions. Man kann auch einfach A := B schreiben (egal wie tief der Record verschachtelt ist) falls der Record nur aus Wertetypen und stat. Array besteht. Listen von Records sind allerdings fehleranfällig, z.B. verschwindet Items[Index].Clear im Nirwana (falls Items eine property ist).


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

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