Autor |
Beitrag |
IhopeonlyReader
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Mo 21.04.14 21:01
Guten Tag,
ich habe jetzt mal ein Server-Client spiel geschrieben.. Server wird pro Client ein paar Byte im Arbeitsspeicher größer, da ich pro Client (in Socket.Data) ein paar Daten ablege, die ich wenn der Client disconnected wieder freigebe...
Leider habe ich, wenn ein Client connected und wieder disconnected 12 Byte (oder sogar KB?) mehr im Arbeitsspeicher..
In der Summe der Clients ist es schnell über 1,5 GB wie ich gemerkt habe :/
ich versuche alles freizzugeben, aber wie soll ich Variablen vom Typ TDatetime oder sontiges freigeben?
Hier mal die Klasse die in die Data des Sockets gehangen werden:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| fName: String; fImSpiel: Boolean; fSpielfeld: TSpielfeld; fDran: Boolean; fGegner: TCustomWinSocket; fFarbe: TBesetzt; fLastSendTime: TDateTime; SSSekTime: TDateTime; SSAnzSend: Cardinal; |
Diese Daten werden auf Connecten des Sockets erstellt:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| PD := TPDaten.Create; PD.fName := ''; PD.fImSpiel := False; PD.fDran := False; PD.fLastSendTime := Now; PD.SSAnzSend := 0; PD.SSSekTime := Now; Socket.Data := PD; |
und wenn er disconnected, gebe ich so frei, leider scheinbar nicht alles:
Delphi-Quelltext 1: 2: 3: 4: 5:
| TPDaten(Socket.Data).fName := ''; Finalize( TPDaten(Socket.Data).fName ); TPDaten(Socket.Data).Free; TPDaten(Socket.Data).fSpielfeld.Free; |
was habe ich vergessen? was muss ich wie freigeben?
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
Xion
      

Beiträge: 1952
Erhaltene Danke: 128
Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
|
Verfasst: Mo 21.04.14 21:43
Klassen müssen freigegeben werden, Records, Arrays und einfache Variablen (z.B. Integer) nicht. Naiv gesprochen: Alles was du von Hand erzeugst (mit .Create oder new() z.B.) muss auch wieder von Hand freigegeben (mit .Destroy/Free oder dispose() z.B.).
Zu deinem angegebenen Code beim Erzeugen genügt zum Freigeben ein
Delphi-Quelltext 1:
| TPDaten(Socket.Data).Free; |
Die Klasse TPDaten muss sich selbst darum kümmern, dass die eigenen privaten Attribute aufgeräumt werden. Dazu schreibst du einfach für die Klasse einen eigenen Destruktur (override nicht vergessen!)
Delphi-Quelltext 1: 2: 3: 4: 5:
| type TPDaten = class [...] public destructor Destroy; override; end; |
Die Klasse weiß ja selbst am besten, ob TSpielfeld eine Klasse ist und dann fSpielfeld.Free aufgerufen werden muss (oder ob es z.B. nur ein Zeiger ist und das Spielfeld nicht gelöscht werden soll).
Wenn du FastMM benutzt, sagt er dir beim Beenden des Programmes, wieviele Bytes Speicher geleaked sind und von welchem Typ sie sind (z.B. in deinem Fall TPDaten), was extrem nützlich ist und die beste mir bekannte Methode, mit Speicherlecks zurecht zu kommen. FastMM wurde mir auch mal hier im Forum empfohlen aus andrem Zusammenhang (es ist auch effizienter was Strings angeht), dem kann ich mich nun anschließen 
_________________ a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
Zuletzt bearbeitet von Xion am Mo 21.04.14 21:45, insgesamt 4-mal bearbeitet
|
|
mandras
      
Beiträge: 432
Erhaltene Danke: 107
Win 10
Delphi 6 Prof, Delphi 10.4 Prof
|
Verfasst: Mo 21.04.14 21:43
Ohne dein Programm näher zu kennen zwei Anmerkungen:
a) Freemem würde ich gar nicht verwenden (oder hast Du irgendwo GetMem eingesetzt?)
c) Wozu das Finalize? fName ist ein normaler langer String, der wird automatisch freigegeben
b) ich sehe eine Fehlermöglichkeit in der Reihenfolge:
Delphi-Quelltext 1: 2:
| TPDaten(Socket.Data).Free; TPDaten(Socket.Data).fSpielfeld.Free; |
tausche die beiden Zeilen einmal gegeneinander aus, möglicherweise ist das das Problem
(weil laut reiner Lehre Socket.Data nach dem Free bereits eigentlich nicht mehr existiert)
Wie gesagt, meine Vermutungen weil ich die Details nicht kenne.
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Mo 21.04.14 21:50
b) ich hatte das Spielfeld schonmal davor freigegen, aber da es nicht immer exisitert, tritt teilweise ein fehler auf und es springt aus dem try-except block und Data.Free würde nicht mehr aufgerufen werden...
ich bringe fSpielfeld.free nun in das destroy ereignis von TPDaten...
aber darin liegt leider nicht der fehler :/
a) Freemem war auch nur ein versuch, nein getmem benutze ich nicht
c) finalize war auch nur ein Versuch, dispose hatte ich auch versucht 
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
Xion
      

Beiträge: 1952
Erhaltene Danke: 128
Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
|
Verfasst: Mo 21.04.14 22:47
IhopeonlyReader hat folgendes geschrieben : | b) ich hatte das Spielfeld schonmal davor freigegen, aber da es nicht immer exisitert, tritt teilweise ein fehler auf und es springt aus dem try-except block und Data.Free würde nicht mehr aufgerufen werden... |
Also das klingt sehr unsauber und äußerst gefährlich. Du rufst ein .Free auf ein Objekt auf, das nicht existiert? Ok, solange dort nil hinterlegt ist, dann ist es nur unsauber, aber wenn nicht, wer weiß was da alles passieren kann. Im schlimmsten Fall ist dort im Speicher jetzt ein andres Objekt selben Typs und es wird gelöscht. Setze es initial auf nil und teste darauf, bevor du Free aufrufst (bzw. Free prüft selbst schon darauf). Es gibt auch FreeAndNil(obj), womit du gleich den Zeiger noch auf nil setzt.
Was dispose angeht kannst du auch mal hier gucken:
www.entwickler-ecke....ewtopic.php?t=111119
_________________ a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Mo 21.04.14 23:10
Leider ist es so,
Wenn 2 Spieler gegeneinander spielen, so wird das Spielfeld erzeugt und bei den Daten beider unter fspielfeld hinterlegt.
Wenn jetzt Spieler 1 und 2 nacheinander disconnecten gibt Spieler 1 das Spielfeld schon frei, Spieler 2 dann unnötiger Weise nochmal
Das fix ich noch, aber darin liegt ja nicht der speicher Fehler an sich...
Und bei einem direkten verbinden und trennen existiert somit kein Spielfeld, darin kann also kein Fehler liegen
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 21.04.14 23:54
Xion hat folgendes geschrieben : | Wenn du FastMM benutzt, sagt er dir beim Beenden des Programmes, wieviele Bytes Speicher geleaked sind und von welchem Typ sie sind (z.B. in deinem Fall TPDaten) |
Und auch, wo der Speicher reserviert wurde inkl. Stacktrace.
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Mo 21.04.14 23:55
Wie muss ich das brnutzen? Habe die ZIP runtergeladen und entpackt.. ( habe delphi 7PE)
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
Xion
      

Beiträge: 1952
Erhaltene Danke: 128
Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
|
Verfasst: Di 22.04.14 08:03
_________________ a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 22.04.14 09:07
Und am besten den FullDebugMode einschalten. Meine entsprechenden Optionen hatte ich einmal hier gepostet:
www.entwickler-ecke.....php?p=682873#682873
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Di 22.04.14 11:01
Xion hat folgendes geschrieben : | Benutzt du gemeinsamen Speicher dafür? Sitzen die Spieler am selben Rechner? Denn sonst sind es zwei vollständig unterschiedliche Objekte. Der Client hat normalerweise eine Kopie der Daten des Severs. Es gibt also zwei unabhängige Spielfelder, die du auch beide freigeben musst. |
Socket.Data ist ein Pointer, hier habe ich die Klasse TPDaten hinterlegt... (Sieht man gut im OnConnect event (siehe Post 1)
TPDaten enthält wiederrum eine variable vom Typ TSpielfeld, da TSpielfeld auch eine Klasse ist, ist fSpielfeld eigentlich nur ein Pointer.
Wenn jetzt Spieler A gegen Spieler B spielt, so wird am Spielstart im Server ein neues Spielfeld (vom Typ TSpielfeld) erzeugt und sowohl bei Spielera in das fSpielfeld als auch bei Spieler B in das fSpielfeld gesetzt... so besitzen beide Spieler in ihren TPDaten ein Pointer auf das Spielfeld.
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Di 22.04.14 11:53
So, der Fehler scheint dank FastMM4 behoben zu sein...
Fehler 1: Beim erstellen wurde ein Stream erstellt, der nicht freigegeben wurde da er mehrmals kopiert wurde.. (nicht im gepostet quelltext zu sehen)
Fehler 2: Beim schließen der Anwendung, wenn noch Clients verbunden waren, wurden diese nicht beendet sondern die Anwendung einfach geschlossen.. nun werden die Verbindungen vorher ordnungsgemäßg beendet und somit auch ihre Daten freigegeben
Ob mein server nun einem DDos angriff ohne Arbeitsspeicherüberlauf aushält wird noch getestet
Allerdings noch ein paar Fragen dazu am Rande...:
a) Wenn etwas nicht zwansläufig existiert (z.B. fSpielfeld vom Typ TSpielfeld und es nicht nil ist, sondern noch ein "veralteter" Wert drin steht, der woanders freigegeben wurde, wie überprüüfe ich ob fspielfeld jetzt noch freigegebn werden muss oder nicht?
aktuell mache ich es so:
Delphi-Quelltext 1: 2: 3: 4:
| try fSpielfeld.free; except end; |
so schmeißt free zwar einen fehler, aber der ist ja egal, da er "abgefangen" wird...
folgendes habe ich probiert:
Delphi-Quelltext 1: 2:
| if (fSpielfeld<>nil) and Assigned(fSpielfeld) then fSpielfeld.Free; |
klappt nur leider nicht :/
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
Nersgatt
      
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Di 22.04.14 11:54
Benutzte statt FSpielfeld.Free einfach FreeAndNil(FSpielfeld).
Dann funktioniert der Test mit Assigned(FSpielfeld) auch zuverlässig.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Di 22.04.14 12:10
naja, es wird ja nicht die variable fSpielfeld zum feigeben verwendet!
beispiel
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| var Spielfeld1, Spielfeld2, DASSpielfeld: TSpielfeld; begin DasSpielfeld := TSpielfeld.create; Spielfeld1 := DAsSpielfeld; Spielfeld2 := DASSpielfeld;
if Random(2)=1 then Spielfeld2.Free; if Assigend( Spielfeld1 ) then Spielfeld1.Free; |
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 22.04.14 13:08
Benutze ein Interface und arbeite dann ausschließlich mit diesem Interface. Dann gibt Delphi dieses automatisch erst frei, wenn die letzte Referenz darauf weg ist. (Wenn das das ist, was du möchtest.)
|
|
Nersgatt
      
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Di 22.04.14 13:10
Nach Deinem Beispielquellcode zeigen DasSpielfeld, Spielfeld1 und Spielfeld2 alle auf die selbe Instanz.
Daher ist es egal, ob Du nun DasSpielfeld, Spielfeld1 oder Spielfeld2 freigibst. Es gibt immer die selbe Instanz frei.
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Di 22.04.14 13:15
ja Nersgatt, das ist es ja.. sie sollen alle auf die selbe instanz zeigen!
Allerdings wird das spielfeld freigegeben, sobald 1 spieler disconnected (da bei einem 1 vs 1) das spielfeld nicht mehr benötigt wird.
Wenn der andere dann auch disconnected, dann will er das spielfeld auch freigeben, obwohl es schon freigegeben ist !
mir geht es nun allgemein um das beispiel, wie ich herausfinde ob ein pointer auf eine freigegebene instanz zeigt.
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
Nersgatt
      
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Di 22.04.14 13:22
IhopeonlyReader hat folgendes geschrieben : | mir geht es nun allgemein um das beispiel, wie ich herausfinde ob ein pointer auf eine freigegebene instanz zeigt. |
Genau wie ich sagte. Verwende FreeAndNil. Damit zeigen hinterher alle Pointer auf nil (die Pointer zeigen ja alle auf die selbe Stelle).
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
IhopeonlyReader 
      
Beiträge: 600
Erhaltene Danke: 23
Delphi 7 PE
|
Verfasst: Di 22.04.14 13:31
wirklich?
ich dachte free and nil würde das machen
fSpielfeld1.Free;
fSpielfeld1 := nil;
weil dann wäre ja nur spielfeld 1 nil und spielfeld2 weiterhin auf die instanz gerichtet, die aber durch free freigegeben wurde
_________________ Sucht "neueres" Delphi
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
|
|
Nersgatt
      
Beiträge: 1581
Erhaltene Danke: 279
Delphi 10 Seattle Prof.
|
Verfasst: Di 22.04.14 13:35
Mea culpa. Du hast Recht. Ich nehm alles zurück und behaupte das Gegenteil 
_________________ Gruß, Jens
Zuerst ignorieren sie dich, dann lachen sie über dich, dann bekämpfen sie dich und dann gewinnst du. (Mahatma Gandhi)
|
|
|