Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Werkzeuge (https://www.delphipraxis.net/63-sonstige-werkzeuge/)
-   -   Memory Leak: Ursache finden (https://www.delphipraxis.net/173304-memory-leak-ursache-finden.html)

Jazzman_Marburg 16. Feb 2013 13:44

Memory Leak: Ursache finden
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo.
Bei der Suche nach der Ursache für einen kleinen Memory-Leak, wäre ich sehr dankbar für jedwede Hilfe.
Nachdem ich mit "ReportMemoryLeaksOnShutDown := True;" die Nachricht (UexpectedMemoryLeak.jpg) über einen Memory-Leak erhalten habe, aber die Ursache nicht finden konnte, habe ich MadExcept mal ausprobiert.
Wie erwartet, gibt es auch einen Leak-Report (LeakReport.jpg) aus dem ich leider auch nicht schlau werde. Wenn ich in diesem Leak-Report auf die im Bild hervorgehobene Zeile im CallStack Doppelklicke, komme ich im meinem Source-Code auf diese Zeile:
Delphi-Quellcode:
  setlength( fHfgkFarbe, fAnzFarben );
Das kann aber doch nicht die Ursache für den Mem-Leak sein, oder?

Jemand eine Idee, was man man aus dem MadExcept noch weiter auslesen kann, um die Urasche für den Memory-Leak auf die Spur zu kommen?

Vielen Dank
Jazzamn

Aphton 16. Feb 2013 14:06

AW: Memory Leak: Ursache finden
 
Kann durchaus möglich sein; zb. wenn der Speicher für den Datentyp dieses Arrays dynamisch alloziert wird (wenn es sich zb. dabei um eine Klasse handelt)

Jazzman_Marburg 16. Feb 2013 14:23

AW: Memory Leak: Ursache finden
 
Hallo.

Vielen Dank - aber das kann ich ausschließen:
Bei
Delphi-Quellcode:
setlength( fHfgkFarbe, fAnzFarben );
wird die Länge eines Array of Integer mittles einfachem Integer (fAnzFarben) bestimmt.

Steht in ähnlicher Form öfters im Source -- schließe ich somit als Verdächtigen aus.

Gruß
Jazzman

stahli 16. Feb 2013 14:29

AW: Memory Leak: Ursache finden
 
Inhaltlich kann ich nicht helfen.
Evtl. könntest Du die Trial von EurekaLog versuchen.
Vielleicht hilft die ja noch genauer?

Aphton 16. Feb 2013 14:55

AW: Memory Leak: Ursache finden
 
Man kann zuerst einmal auch manuell den Code durchscannen, sofern es sich noch um kein allzu umfangreiches Projekt handelt.
Such einfach mal nach ".Create" und "GetMem"/"New" o.Ä.
Überprüfe anschließend, ob letzendlich alles allozierte auch zu 100% freigegeben wird.

Union 16. Feb 2013 15:01

AW: Memory Leak: Ursache finden
 
Zitat:

Zitat von Jazzman_Marburg (Beitrag 1203844)
Hallo.
Steht in ähnlicher Form öfters im Source -- schließe ich somit als Verdächtigen aus.
Gruß
Jazzman

Warum? MadExcept arbeitet ja nicht nach dem Zufallsprinzip. Also mal gucken: Wie ist fHfgkFarbe definiert? Ist es eine property, eine private oder was sonst? Wird das vielleicht irgendwo vorher auf nil gesetzt?

sx2008 16. Feb 2013 16:52

AW: Memory Leak: Ursache finden
 
Ein kleiner Tipp: durchsuche den gesamten Sourcecode nach "
Delphi-Quellcode:
destructor Destroy;
"; wenn irgendwo der Zusatz "
Delphi-Quellcode:
override;
" fehlt, dann ist das ein potentielles Speicherleck.

Zusätzlich kann man noch nach "destructor T" suchen und prüfen ob in jedem Destruktor auch das
Delphi-Quellcode:
inherrited
aufgerufen wurde.

Jazzman_Marburg 16. Feb 2013 17:12

AW: Memory Leak: Ursache finden
 
Zitat:

Zitat von sx2008 (Beitrag 1203866)
Ein kleiner Tipp: durchsuche den gesamten Sourcecode nach "
Delphi-Quellcode:
destructor Destroy;
"; wenn irgendwo der Zusatz "
Delphi-Quellcode:
override;
" fehlt, dann ist das ein potentielles Speicherleck.

Zusätzlich kann man noch nach "destructor T" suchen und prüfen ob in jedem Destruktor auch das
Delphi-Quellcode:
inherrited
aufgerufen wurde.

Ein sehr guter Tipp -- leider kein Destructor ohne override, und in jedem Destructor ist stets ein "inherited Destroy;"
Ich dachte schon, dass muß es sein... Dennoch ein guter Tipp!

Zitat:

Zitat von Union (Beitrag 1203851)
[...] Warum? MadExcept arbeitet ja nicht nach dem Zufallsprinzip. Also mal gucken: Wie ist fHfgkFarbe definiert? Ist es eine property, eine private oder was sonst? Wird das vielleicht irgendwo vorher auf nil gesetzt?

Diesen Hinweis verstehe ich nicht, ich sagte ja, bei fHfgkFarbe handelt es sich um ein Array of Integer -- und ja, es ist ein private Klassenvariable. Ein Nil ist nirgends zu finden.

Zitat:

Zitat von Aphton (Beitrag 1203850)
Man kann zuerst einmal auch manuell den Code durchscannen, sofern es sich noch um kein allzu umfangreiches Projekt handelt.
Such einfach mal nach ".Create" und "GetMem"/"New" o.Ä.
Überprüfe anschließend, ob letzendlich alles allozierte auch zu 100% freigegeben wird.

Jou -- das war mein Vorgehen, bevor ich das hier postete.

Es sind nicht allzu viele Klassen und Units -- vielleicht mal eine kleine Pause -- und dann mit Kaffee...

Vielen Dank euch allen!

Jazzman

Union 16. Feb 2013 17:25

AW: Memory Leak: Ursache finden
 
Versuch mal im destructor der Klasse ein
Delphi-Quellcode:
SetLength(fHfgkFarbe, 0)

Jazzman_Marburg 16. Feb 2013 18:32

AW: Memory Leak: Ursache finden
 
Zitat:

Zitat von Union (Beitrag 1203869)
Versuch mal im destructor der Klasse ein
Delphi-Quellcode:
SetLength(fHfgkFarbe, 0)

Keinerlei Wirkung -- ReportMemoryLeak ("37-44 Bytes: Unknown x 1") unverändert.
Delphi-Quellcode:
destructor TPaletto.Destroy;
begin

  inherited Destroy;
  SetLength(fHfgkFarbe, 0);

end;
Danke!

Klaus01 16. Feb 2013 18:35

AW: Memory Leak: Ursache finden
 
Zitat:

Zitat von Jazzman_Marburg (Beitrag 1203874)
Zitat:

Zitat von Union (Beitrag 1203869)
Versuch mal im destructor der Klasse ein
Delphi-Quellcode:
SetLength(fHfgkFarbe, 0)

Keinerlei Wirkung -- ReportMemoryLeak ("37-44 Bytes: Unknown x 1") unverändert.
Delphi-Quellcode:
destructor TPaletto.Destroy;
begin

 
  SetLength(fHfgkFarbe, 0);
  inherited Destroy;
end;
Danke!

.. und wenn Du inherited Destroy als letztes aufrufst?

Grüße
Klaus

Union 16. Feb 2013 18:38

AW: Memory Leak: Ursache finden
 
Und so?
Delphi-Quellcode:
destructor TPaletto.Destroy;
begin
  SetLength(fHfgkFarbe, 0);
  fHfgkFarbe := nil;
  inherited Destroy;
end;

Sir Rufo 16. Feb 2013 18:52

AW: Memory Leak: Ursache finden
 
Ein Array (dynamisch oder statisch) muss nicht freigeben werden. Das ist also mal nicht der Grund.

Delphi-Quellcode:
Unknown
(ganz alleine für sich) ist auch eher ein Hinweis auf einen mit
Delphi-Quellcode:
GetMem
/
Delphi-Quellcode:
AllocMem
allozierten Speicher-Bereich.

Sourcen möchtest du aber wohl hier nicht reinstellen?

Dann wird es schwierig, denn so ist das ein Blindflug

Jazzman_Marburg 16. Feb 2013 21:22

AW: Memory Leak: Ursache finden
 
Zitat:

Zitat von Union (Beitrag 1203876)
Und so?
Delphi-Quellcode:
destructor TPaletto.Destroy;
begin
  SetLength(fHfgkFarbe, 0);
  fHfgkFarbe := nil;
  inherited Destroy;
end;

Dito - exakt der selbe Leak-Report.

Zitat:

Zitat von Sir Rufo (Beitrag 1203877)
[...]
Delphi-Quellcode:
Unknown
(ganz alleine für sich) ist auch eher ein Hinweis auf einen mit
Delphi-Quellcode:
GetMem
/
Delphi-Quellcode:
AllocMem
allozierten Speicher-Bereich.

Sourcen möchtest du aber wohl hier nicht reinstellen?

Dann wird es schwierig, denn so ist das ein Blindflug

Ein explizites
Delphi-Quellcode:
GetMem
/
Delphi-Quellcode:
AllocMem
nutze ich nicht.

Sourcen wären im Prinzip überhaupt kein Problem -- aber es ist doch einiges an Code, und das wäre wirklich nicht ok, euch meinen Fehler im meinem Code suchen zu lassen. Sehr lieb!

Ich werde morgen einfach mal eine große Auskommentierungsaktion starten und mal systematisch rang gehen.

Ich bin nur ein wenig enttäuscht von MadExcept, so dass es mir wirklich gar kein Hinweis geben konnte. Ist technisch aber sicher auch nicht ganz einfach.

Vielen Dank an alle - super Truppe :thumb:

Gruß
Jazzman

EgonHugeist 16. Feb 2013 22:20

AW: Memory Leak: Ursache finden
 
StrNew(), ReallocMem() ???

Ist dein Project irgendwie FPC portable? Hier ist FPC Meilen vorraus mit deren HeapTracing. Hier kann man fast ganz exakt festsetellen, wo die MemLeaks "offen" bleiben und deren Ursache ergründen. Benutzt du noch eine Ansi-Delphi? Dann könnte die MemCheck.pas genauere Infos geben..

Union 17. Feb 2013 12:33

AW: Memory Leak: Ursache finden
 
Gibst Du das TPaletto Objekt denn überhaupt frei?

Uwe Raabe 17. Feb 2013 12:41

AW: Memory Leak: Ursache finden
 
Zitat:

Zitat von Union (Beitrag 1203954)
Gibst Du das TPaletto Objekt denn überhaupt frei?

Das wäre auch meine Frage: landest du überhaupt im Destroy?

Jazzman_Marburg 17. Feb 2013 14:05

AW: Memory Leak: Ursache finden
 
Problem gelöst! :-D

Wie schon alle Tools (und auch eure Hinweise) daraufhin deuteten, hing der Memory Leak mit dem Array of Integer zusammen:

Delphi-Quellcode:
FillChar( fHfgkFarbe, SizeOf( fHfgkFarbe ), 0);
Das Array wurde anschließend überhaupt nicht benutzt (stammt noch aus einer vorherigen Version), und genau diese Zeile sorgte für den Memory-Leak. Eine Recherche in einschlägigen Foren brachte dann auch den Hinweis, dass ein FillChar zum Initialisieren von Arrays nur mit Vorsicht zu benutzen ist.
Wenn ich nun das Array manuell mit Nullen initialisiere ist alles ok.
Wäre ja schon schön, wenn man eine "sichere" Methode hätte, Arrays mit Nullen zu füllen, wenn man schon FillChar nur unter bestimmten Umständen nutzen kann.

Aber das Problem ist gelöst, und ich danke allen Helfern!

Gruß
Jazzman

Union 17. Feb 2013 14:15

AW: Memory Leak: Ursache finden
 
Schön dass Du es gefunden hast. Initialierung bei einem dynamischen Array of integer wäre ja wohl eher:
Delphi-Quellcode:
FillChar( fHfgkFarbe, SizeOf(Integer) * Length(fHfgkFarbe), 0);

Blup 18. Feb 2013 17:00

AW: Memory Leak: Ursache finden
 
Variablen vom Typ eines dynamischen Arrays sind Zeiger auf den Speicher in dem die Array-Elemente liegen.
Delphi-Quellcode:
FillChar(fHfgkFarbe, {...}
Das ist falsch, so wird der Zeiger selbst überschrieben, nicht der Inhalt des Arrays.

Richtig so:
Delphi-Quellcode:
if Length(fHfgkFarbe) > 0 then
  FillChar(fHfgkFarbe[0], SizeOf(fHfgkFarbe[0]) * Length(fHfgkFarbe), 0);

sx2008 22. Feb 2013 15:20

AW: Memory Leak: Ursache finden
 
Zitat:

Zitat von Jazzman_Marburg (Beitrag 1203892)
Ich bin nur ein wenig enttäuscht von MadExcept, so dass es mir wirklich gar kein Hinweis geben konnte. Ist technisch aber sicher auch nicht ganz einfach.

Je mehr man auf Klassen setzt, umso leichter wird die Fehlersuche.
Man kann z.B. dynamischee Arrays direkt in der Anwendung, der "Businesslogik", benützen.
Oder man kapselt das Array innerhalb einer Klasse und lässt nur einen kontrollierten Zugriff über die Methoden der Klasse zu.
Insbeondere sollte man Resourcen (Speicher ist auch eine Resource) immer unter die Kontrolle einer Klasse stellen.
Das heisst dann konkret, dass AllocMem und FreeMem nur im geschützten Kontext einer Klasse aufgerufen werden.
Dann testet man diese Klasse in einer isolierten Umgebung (aka Testprogramm) und kann so sicherstellen, dass die Klasse in sich keine Speicher- oder Resourcenlecks hat.

Findet man später in der Anwendung ein Speicherleck ist die Wahrscheinlichkeit höher, dass man den Klassennamen angezeigt bekommt und so gezielt suchen kann.

Manchmal bekommt man nur allgemeine Klassennamen gemeldet (z.B. TStringList x 14), dann kann man auch von TStringList abgeleitete Klassen einsetzen.
Kleines Beispiel dazu:
Delphi-Quellcode:
// Klasse zum Laden von Daten aus einer Datei
// wird zum Datenimport verwendet
// die Datei darf nicht leer sein
TImportStringList = class(TStringList)
public
  procedure LoadFromFile(const FileName: string); override;
end;

procedure TImportStringList.LoadFromFile(const FileName: string);
begin
   inherited;
   if count = 0 then
      raise EBadImportFile.CreateFmt('Datei %s ist leer', [FileName]);
end;
So schlägt man zwei Fliegen mit einer Klappe.
Man kann kleine Teile der Funktionalität an TImportStringList übertragen (prüfen ob Datei leer ist) und ausserdem bekommt man bei einem Speicherleck gezielte Info wo zu suchen ist.

Die ganzen Punkte oben gelten speziell für sehr grosse Anwendungen mit Hunderten von Units.
Bei kleinen Anwendungen braucht man nicht so viel Aufwand zu treiben.

BerlinärBär 28. Feb 2013 21:05

AW: Memory Leak: Ursache finden
 
Hallo zusammen,

falls sich noch jemand für die Erklärung interessiert:
Es ist wirklich das 'FillChar', aber nur, weil es falsch angewandt wurde:
Statt 'FillChar( fHfgkFarbe, SizeOf( fHfgkFarbe ), 0)'
muss es 'FillChar( fHfgkFarbe[0], Length(fHfgkFarbe)*SizeOf(ein element davon), 0)'
heißen. Die obere Zeile leert nur die Feldvariable, aber der damit verbundene Speicherblock mit dem eigentlichen Feldinhalt ist ab dann für den Delphi-Speichermanager unsichtbar.

Gruß vom Bären!

Blup 1. Mär 2013 10:29

AW: Memory Leak: Ursache finden
 
@BerlinärBär Die Ursache für das Speicherleck wurde schon in #20 genannt.

BerlinärBär 1. Mär 2013 18:37

AW: Memory Leak: Ursache finden
 
@Blup: Ups, war wohl selektives Lesen...


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:42 Uhr.

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