![]() |
Memory Leaks beheben
Hallo zusammen!
Bei meinem Programm treten andauernd memory leaks auf, und das nicht zu knapp :D Hatte schon eine Auslastung von >500mb. Das heisst also, dass irgendwo ziemlich schlecht programmiert wurde :roll: Allerdings weiss ich nicht genau wo bzw. was das Problem ist. Das Programm ist auch nich gerade klein (über 15 Units) und benutzt unter anderem auch noch Andorra 2d. Wie behebt man soetwas? Ich hab schon gegoogelt und Programme gesucht die mir das anzeigen, allerdings hilft mir das auch nicht wirklich weiter. Gibt es für Delphi nicht evtl. so eine Art Addon eines Garbage Collectors wie bei C? Meine Fragen sind evtl. etwas übergreifender, und nicht mal eben zu erklären, aber vll. hat jemand Links oder Ähnliches die mir weiterhelfen könnten? (Auf einem niedrigen Niveau bitte, bin kein Profi wie Ihr hier.. eher das Gegenteil... :lol:) Wäre nett =) MfG |
AW: Memory Leaks beheben
Du solltest erstmal
![]() Dann kannst du eventuell schon eher einschätzen, was zum Problem werden könnte. MfG Fabian |
AW: Memory Leaks beheben
wenn ich in From1.Create ReportMemoryLeaksOnShutdown:=true setze kommt da undefinierter bezeichner.
welche unit muss ich dafür einbinden? |
AW: Memory Leaks beheben
Keine, da es in der implizit eingebunden Unit System definiert ist. Aber eben erst bei neueren Delphi-Versionen.
|
AW: Memory Leaks beheben
hab delphi 7
wenn ich System enbinden will kommt Bezeichner redefiniert. und nu? |
AW: Memory Leaks beheben
Ich sagte ja, System ist implizit eingebunden. Selbst wenn du die ganze Uses-Klausel entfernst, System wäre trotzdem eingebunden :wink:
Ich glaube FastMM wäre eine Möglichkeit. Bin da aber nicht gerade der beste Ansprechpartner für. Such doch einfach mal hier im Forum z.B. nach "Speicherleck" ... |
AW: Memory Leaks beheben
Suche mal in der DP nach "FastMM4" und "Optionen".
In der Ini von FastMM4 kannst Du einstellen, dass Dir eine komplexe Report-Datei erzeugt wird. Habe es aber jetzt nicht im Detail parat... |
AW: Memory Leaks beheben
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo nochmal.
Habe jetzt ein Programm welches mir anzeigt, in welcher Zeile des Codes die Leaks auftreten.. Es sind so einige Leaks :D direkt nach dem Start sogar 221 :( Davon kommen die meisten aber nur 1x vor, im Anhang befindet sich ein Ausschnitt von denen, die am Häufigstens vorkommen. Allerdings sehen die meisten Zeilen die mir das Programm liefert dann so aus:
Delphi-Quellcode:
Die meisten Fehler spuckt also Andorra 2D aus.
AdImageList.LoadFromFile( 'bilder/bilder.ail' );
temp := TAdImage.Create(FParent); Add(temp); LoadFromStream(ms); Rects.Add(AdRect(0,0,FWidth,FHeight)); new(ar); ar^ := ARect; inherited Add(ar); LoadFromXML(xml.Root); Allerdings weiss ich jetz überhaupt nicht wie ich die beheben soll, bzw. wie Andorra anders nutzen soll!? Andorra müsste aber doch eig. funktionieren, da es viele Spiele gibt die damit programmiert wurden etc. Bei mir steigt der Speicher zum Teil pro sekunde um 4-5 mb!!! Hilfe :pale: |
AW: Memory Leaks beheben
Gibst du die Form, bzw. Andorra auch wieder frei?
Jedenfalls steht da in dem Bild irgendwas von Form1 mit drin. Zitat:
Zitat:
Zitat:
|
AW: Memory Leaks beheben
Hallo,
Delphi-Quellcode:
Hast du auch irgendwo ein AdImageList.Clear bzw. AdImageList.Free ?
AdImageList.LoadFromFile( 'bilder/bilder.ail' );
Du lädst einen Haufen Sachen, erzeugst Speicher (new ()ar), aber ich sehe nirgendwo eine Freigabe nach der Benutzung. Unter D7 könntest du auch memcheck benutzen. Das hat den Vorteil, dass beim Beenden per Exeption die Stellen angeprungen werden, wo er Speicher angefordert wurde. Heiko |
AW: Memory Leaks beheben
Hi,
mir ist nur ein Memoryleak in Andorra 2D bekannt, und das tritt unter Umständen beim Reinitialisieren von Andorra 2D auf (also TAdDraw.Finalize und Initialze in einer Kette). Die von deinem Leak-Checker angezeigten Zeilen deuten aber eher darauf hin, dass du einfach manche Objekte nicht richtig frei gibst - um genau zu sein deine Imagelist, da diese natürlich eine Reihe von TAdImages lädt und die TAdImages wiederum in den aufgelisteten Operationen Speicher reservieren. Die TAdImagelist würde dann auch die Images wieder frei geben und diese den von sich reservierten Speicher. Hoffe das hilft, Andreas |
AW: Memory Leaks beheben
Neben den schon erwähnten FastMM4 und MemCheck o.ä. solltest Du dir angewöhnen, das, was Du allokierst auch im gleichen Kontext freizugeben.
Beispiel:
Delphi-Quellcode:
Var
localObject : TSomeOBject; Begin localObject := TSomeObject.Create; Try DoSomethingWith(localObject); Finally localObject.Free End End; ... Type TSomeObject = Class (TSomeOtherObject) private fObject : TFoobar; ... Constructor TSomeObject.Create; begin inherited; fObject := TFoobar; end; Destructor TSomeObject.Destroy; Begin fObject.Free; inherited End; |
AW: Memory Leaks beheben
Danke für all Eure Tips, die haben mir schonmal weiter geholfen.
@igel457 Du hast Recht, die Imagelist wurde nicht freigegeben. VOn Andorra2d aus treten jetzt keine Memory Leaks mehr auf. Ich konnte den Leak auf folgende Prozedur zurückführen:
Delphi-Quellcode:
Allerdings verstehe ich nicht genau wieso hier ein Memory Leak auftritt.
function Textwerkzeug.wortAn( pText: string; wortNummer: integer ): string;
var lText: string; begin if copy( pText, length( pText ) - 1, 2 ) = CRLF then pText := copy( pText, 1, length( pText ) - 2 ); //LEAK lText := pText; while ( ansiPos( zTrennung, lText ) > 0 ) and ( wortNummer > 1 ) do begin delete( lText, 1, ansiPos( zTrennung, lText ) + length( zTrennung ) - 1 ); dec( wortNummer ); end; if wortNummer = 1 then if ansiPos( zTrennung, lText ) > 0 then result := copy( lText, 1, ansiPos( zTrennung, lText ) - 1 ) //LEAK else result := lText else result := ''; end; Das (pText/result über den copy Befehl) sind doch strings und keine Objekte? Wie soll man die denn freigeben/löschen? |
AW: Memory Leaks beheben
Delphi-Quellcode:
riecht ein wenig danach, als ob du den irgendwo als PChar o.ä. definiert hast und damit dann die Procedure aufrufst.
pText
Musst du das veränderte pText zwingend wieder zurückgeben? Wenn nicht, dann kannst du das auch so machen:
Delphi-Quellcode:
Eigentlich sollte man es vermeiden, die Parameter zu verändern es sei denn, die sollen auch bewusst wieder zurückgeliefert werden. (wortNummer)
function Textwerkzeug.wortAn( const pText: string; wortNummer: integer ): string;
var lText: string; begin if copy( pText, length( pText ) - 1, 2 ) = CRLF then lText := copy( pText, 1, length( pText ) - 2 ) else lText := pText; while ( ansiPos( zTrennung, lText ) > 0 ) and ( wortNummer > 1 ) do begin delete( lText, 1, ansiPos( zTrennung, lText ) + length( zTrennung ) - 1 ); dec( wortNummer ); end; if wortNummer = 1 then if ansiPos( zTrennung, lText ) > 0 then result := copy( lText, 1, ansiPos( zTrennung, lText ) - 1 ) //LEAK else result := lText else result := ''; end; Dann sollte man vor die Parameter aber auch ein
Delphi-Quellcode:
setzen.
var
Beim Benutzen der Func/Proc ist es einem dann auch bewusst, dass der Übergabeparameter nachher verändert sein kann. |
AW: Memory Leaks beheben
Könnte auch Parameter heißen.
(nja, dieses Typen-Prefix, welches du meins, ist eh eine Unsitte) Was soll das ![]() |
AW: Memory Leaks beheben
Darum ja auch nur "es riecht danach" ;)
|
AW: Memory Leaks beheben
Liste der Anhänge anzeigen (Anzahl: 1)
Also die Unit die ich da benutze, habe ich nicht selbst geschrieben.
Es ist Textwerkzeugunit die ich im Internet gefunden habe. (Ist im Anhang) Aufgerufen wird das ganze in dieser Prozedur:
Delphi-Quellcode:
procedure Verbindung.ClientSocketRead( Sender: TObject; Socket: TCustomWinSocket );
var lNachricht, lEineNachricht: Zeichenkette; begin lNachricht := Socket.receiveText; hatTextwerkzeug.setzeTrennung( NTrenner ); repeat lEineNachricht := hatTextwerkzeug.wortAn( lNachricht, 1 ); //LEAK if hatTextwerkzeug.laenge( lEineNachricht ) = hatTextwerkzeug.laenge( lNachricht ) then lNachricht := '' else lNachricht := hatTextwerkzeug.textOhne( lNachricht, 1, hatTextwerkzeug.laenge( lEineNachricht ) + hatTextwerkzeug.laenge( NTrenner ) ); if lEineNachricht <> '' then hatListe.haengeAn( ZeichenketteObjekt.init( lEineNachricht ) ); if zMitProtokoll then hatClientKontrollfenster.memo1.lines.add( 'Verbindung liest ' + lEineNachricht ); until lNachricht = ''; end; |
AW: Memory Leaks beheben
Wo wird
Delphi-Quellcode:
initialisiert und wieder freigegeben?
hatTextwerkzeug
Wie ist
Delphi-Quellcode:
definiert?
Zeichenkette
|
AW: Memory Leaks beheben
Liste der Anhänge anzeigen (Anzahl: 1)
hatTextwerkzeug wird in
Delphi-Quellcode:
initialisiert.
constructor Verbindung.init( pIPAdresse: Zeichenkette; pPortNr: GanzeZahl;
pMitProtokollieren: Wahrheitswert ); begin hatTextwerkzeug := Textwerkzeug.init; .. end;
Delphi-Quellcode:
Zeichenkette ist ein string
ZeichenketteObjekt = class
private zInhalt: Zeichenkette; public constructor init( pInhalt: Zeichenkette ); procedure setzeInhalt( pInhalt: Zeichenkette ); function Inhalt: Zeichenkette; destructor gibFrei; end; constructor ZeichenketteObjekt.init( pInhalt: Zeichenkette ); begin zInhalt := pInhalt; end; procedure ZeichenketteObjekt.setzeInhalt( pInhalt: Zeichenkette ); begin zInhalt := pInhalt; end; function ZeichenketteObjekt.Inhalt: Zeichenkette; begin result := zInhalt; end; destructor ZeichenketteObjekt.gibFrei; begin end; |
AW: Memory Leaks beheben
Das ist ja gruselig. Zum Einen heißt der vorgegebene Destruktor nicht GibFrei, sondern Destroy und sollte überschrieben werden. Zum Anderen: wenn schon eine Klasse, dann auch bitte mit Properties und entsprechenden Getter- und Setter-Methoden. Mir scheint, da hat sich jemand diese komische mSum-Unit genau angeschaut und abgekupfert.
|
AW: Memory Leaks beheben
Ja, ich weiss es auch nicht.
Ich komme auch nicht mehr weiter, bzw. weiss nicht was ich tun soll... Wenn das Programm läuft, steigt einfahc die ganze Zeit die Ram Auslastung... und irgendwann stürzt es dann halt ab... |
AW: Memory Leaks beheben
Wozu zum Henker wird denn dieses komische Zeichenkettenobjekt benötigt, wenn es doch nichts Besonderes tut?
|
AW: Memory Leaks beheben
hier:
Delphi-Quellcode:
Allerdings kann ich nicht auf gibfrei, free oder sonst was zugreifen.
procedure Verbindung.ClientSocketRead( Sender: TObject; Socket: TCustomWinSocket );
var lNachricht, lEineNachricht: Zeichenkette; begin lNachricht := Socket.receiveText; hatTextwerkzeug.setzeTrennung( NTrenner ); repeat lEineNachricht := hatTextwerkzeug.wortAn( lNachricht, 1 ); if hatTextwerkzeug.laenge( lEineNachricht ) = hatTextwerkzeug.laenge( lNachricht ) then lNachricht := '' else lNachricht := hatTextwerkzeug.textOhne( lNachricht, 1, hatTextwerkzeug.laenge( lEineNachricht ) + hatTextwerkzeug.laenge( NTrenner ) ); if lEineNachricht <> '' then hatListe.haengeAn( ZeichenketteObjekt.init( lEineNachricht ) ); //HIER if zMitProtokoll then hatClientKontrollfenster.memo1.lines.add( 'Verbindung liest ' + lEineNachricht ); until lNachricht = ''; end; |
AW: Memory Leaks beheben
Hier handelt es sich wieder um diese verhunzte (mir liegt ja eher vergewaltigte) Unit-Sammlung aus dem Schulunterricht.
Helfen kann man da nur wenig, denn diese Sammlung ist - nicht wirklich Standard - teilweise sogar falsch aufgebaut - verdeutscht mit bisserl englisch - wahrscheinlich auch noch ansteckend :mrgreen: - und hier im Forum auch nicht bekannt (aus oben genannten Gründen) nochmal zu hatTextwerkzeug: Wo wird das wieder freigegeben? |
AW: Memory Leaks beheben
ReceiveText gibt einen String zurück. Ich wundere mich eigentlich, dass der Code überhaupt kompiliert wird. Nochmal: wozu diese komische Klasse, wenn man genausogut den Datentyp String verwenden kann?
|
AW: Memory Leaks beheben
Zitat:
lNachricht ist vom Typ Zeichenkette und das ist String ZeichenketteObject ist ein Object ;) |
AW: Memory Leaks beheben
:wall: Wo liegt eigentlich der Sinn darin, Bezeichner für bereits existierende Datentypen unbedingt eindeutschen zu wollen? Da muss man ja durcheinanderkommen.
|
AW: Memory Leaks beheben
Zitat:
![]() |
AW: Memory Leaks beheben
[OT] Man müsste denjenigen, der diese Units verbrochen hat, dazu zwingen, "Jehova" zu sagen :mrgreen: [/OT]
|
AW: Memory Leaks beheben
Wenigstens sollte ein CompilerSchalter drin sein:
Delphi-Quellcode:
{$IFDEF Lehrer}
... {$ELSE} ShowMessage( 'Merkst du es noch?' ); {$ENDIF} |
AW: Memory Leaks beheben
Nun gut :D
wir halten also fest: Die Units sind vermurkst. Trotzdem muss man doch den MemoryLeak da raus bekommen können oder? Zeichnkette Objekt wird hier freigegeben:
Delphi-Quellcode:
entferneAktuelles macht folgendes:
procedure Tclientverbindung.ClientSocketRead( Sender: TObject; Socket:
TCustomWinSocket ); var lNachricht: Zeichenkette; begin inherited ClientSocketRead( Sender, Socket ); while not hatListe.istLeer do begin hatListe.zumAnfang; lNachricht := ZeichenketteObjekt( hatListe.aktuelles ).inhalt; hatListe.entferneAktuelles; //Hier denke ich zumindest bearbeiteNachricht( lNachricht ); end; end;
Delphi-Quellcode:
Tut mir Leid, wenn ich euch mit diesem ganzen Kram "belästige" ;)
procedure Liste.entferneAktuelles;
var lZeiger: Knoten; begin if not ( istDavor or istDahinter ) then begin if hatAktuelles = hatAnfang then hatAnfang := hatAnfang.nachfolger else begin hatAktuelles.vorgaenger.setzeNachfolger( hatAktuelles.nachfolger ); hatAktuelles.nachfolger.setzeVorgaenger( hatAktuelles.vorgaenger ); end; lZeiger := hatAktuelles; hatAktuelles := hatAktuelles.nachfolger; lZeiger.free; //Hier wird es freigebgen dec( zLaenge ); end end; Wie schon richtig vermutet, habe ich diese Units aus der Schule... Das dieses ganze Eingedeutsche nicht so schön ist, dem würde ich ebenfalls zustimmen. Was gibt es denn für Alternativen? Die Units werden dafür gebraucht eine Netzwerkverbindung aufzubauen und zwischen einem Server und Clients Daten auszutauschen. Gibt es evtl. andere Projekte/BibliothekenProgramme die "schöner" sind? MfG |
AW: Memory Leaks beheben
Da scheint auch noch eine doppelt verkettete Liste im Spiel zu sein :pale:. Schmeiß doch die ganzen Schulunits mal komplett aus dem Projekt (und aus dem Gedächtnis) und fang von vorn an. Du könntest Dir z.B. mal die Demos von Delphi ansehen, da war IIRC auch eins mit Sockets dabei, ansonsten zieh Dir mal die
![]() |
AW: Memory Leaks beheben
Deine Vermutung ist falsch, da wird kein relevantes Objekt freigegeben.
Es geht hier leider nicht um "schön" sondern um falsch. Somit kann nicht ausgeschlossen werden, dass die MemLeaks von den vergewaltigten Destruktoren kommt. Ein Destruktor wird so implementiert und nicht anders
Delphi-Quellcode:
Macht man das anders dann kann es rumsen (MemLeaks).
destructor Destroy; override;
... destructor TFoo.Destroy; begin inherited; end; Desweiteren muss man immer aufpassen wo/wer ein Objekt erzeugt. Da/der sollte sich auch um die Freigabe kümmern. Und natürlich kann man die MemLeaks da auch rausbekommen - komplett neuschreiben ohne diese Units - alle Units durcharbeiten, korrigieren und dann den Source überprüfen Ich würde mich aber wundern, wenn du hier jemand findest, der das für Dich übernimmt, denn das ist in 5-10 Minuten nicht erledigt und du wirst wahrscheinlich auch keine Lust haben dafür zu bezahlen, genauso wie hier wohl keiner Lust hat ein paar Tage daran herumzuschrauben. Sorry to tell bad news |
AW: Memory Leaks beheben
Na gut, dann aber schon mal vielen Dank für Eure Hilfe!!!
Ich werd mich mal mit den Tutorials von Delphi-Treff befassen. Noch ein schönes Restwochenende ;) |
AW: Memory Leaks beheben
Hallo nochmal!
Ich lese geradefolgendes Tutorial über Multithreading: ![]() Kann man mit Threads MemLeaks beheben, in dem man die kritischen Stellen einfach in einen Thread packt, welcher später freigegeben wird? Diese Stelle hat mich auf den Gedanken gebracht: Zitat:
|
AW: Memory Leaks beheben
Zitat:
FreeOnTerminate bezieht sich nur auf das Thread-Objekt. |
AW: Memory Leaks beheben
MemoryLeaks kann man nur durch gewissenhafte Programmierung verhindern.
|
AW: Memory Leaks beheben
Ich weis ja nicht, in wie weit du das bisherige schon verworfen hast, aber hier ich hab noch einen Fehler gefunden:
Edit: doch nicht... |
AW: Memory Leaks beheben
hello again!
Mir ist aufgefalle, dass der Memory Leak erst dann so extrem wird, wenn ein Spieler (Raumschiff) stirbt. -> d.h. es liegt sehr wahrscheinlich doch nicht an den Netzwerkklassen, sondern daran, wie dieses Raumschiff gelöscht wird. Ich poste mal eben wie es created und destructed wird, evtl. ist dort ein Fehler den ich nicht sehe:
Delphi-Quellcode:
Nun zum interessanten Teil, dem Löschen:
TRaumschiffSprite = class( TMySprite )
public nick: string; farbe: TColor; leben, lebenMax: Extended; gesundheitsbalken: TBalken; spielerid: integer; constructor Create( AParent: TSprite ); override; end; procedure TSpriteVerwaltung.neuesRaumschiff( pID: Integer; pSpielerID: integer; pFarbe: TColor; pX, pY, pLaenge, pKurs: Integer; pVX, pVY, pAX, pAY, pLeben, pLebenMax: Extended ); // Erstellt ein neues Raumschiff, weist ihm die entsprechenden Eigentschaften zu // und fügt es in die Liste ein var raumschiff: TRaumschiffSprite; particle: TAdBillboardParticle; begin raumschiff := TRaumschiffSprite( getSprite( pID ) ); // es wird nur dann created, wenn das Raumschiff nicht bereits exisitiert. //wenn es existiert werden nur unten die Werte wie x,y positiongeupdated etc. if raumschiff = nil then begin raumschiff := TRaumschiffsprite.Create( form1.AdSpriteEngine ); //hier wird das Raumschiff created raumschiff.id := pID; raumschiff.gesundheitsbalken := TBalken.Create( form1.AdSpriteEngine, 26, 3 ); Add( raumschiff ); end; with raumschiff do begin spielerid := pSpielerID; farbe := pFarbe; Angle := pKurs; x := pX - Width / 2; y := py - Height / 2; vx := pVX; vy := PVY; ax := pAX; ay := pAY; color := farbe; leben := pLeben; lebenMax := pLebenMax; gesundheitsbalken.x := x + 7; gesundheitsbalken.y := y + 40; gesundheitsbalken.Aktualisieren( lebenMax, leben, pVX, pVY, pAX, pAY ); // Lebensbalken aktualisieren (Breiten, v, a) end; end;
Delphi-Quellcode:
Also wie gesagt, nach dem das erste mal ein Raumschiff zerstört wird, tritt dieser extreme Memory Leak auf.
function TBefehlLoeschen.Ausfuehren( pParameter: string ): boolean;
var lID: integer; lSprite: TImageSpriteEx; begin try //Sprite in der Liste suchen und das Listenobjekt löschen lID := StrToInt( pParameter ); lSprite := Form1.SpriteVerwaltung.getSprite( lID ); Form1.SpriteVerwaltung.Remove( lSprite ); if lSprite is TRaumschiffSprite then begin TRaumschiffSprite( lSprite ).gesundheitsbalken.Free; end; //löschen des eigentlichen Sprites über Andorra lSprite.dead; result := true; except on E : Exception do ShowMessage(E.ClassName+' error raised, with message : '+E.Message); result:=false; end; end; Ist irgendwas mit meiner Löschen Methode falsch? Wäre sehr nett, wenn Ihr mir nochmal weiterhelfen könntet :) MfG |
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:53 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