Memory Leaks..

Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Neue Antwort erstellen

FOODy

Betreff: Memory Leaks..

BeitragMi, Feb 14, 2007 0:53
Antworten mit Zitat
Benutzer-Profile anzeigen
Hiho.
Ich hab ein Problem das mich schon länger beschäftigt: Memory Leaks.
Habe mich immer gewundert wie die entstehen.
Der Destruktor müsste doch alles zu "Null"en....

Ich hab ein kleinen Code geschrieben, der ein Leak entstehen lässt.
Falls der Fehler an mir liegt, bitte bescheid geben Very Happy
Mir geht das einfach nur übelst auf den Sack und ich möcht das Problem entlich abspülen.

Code: [AUSKLAPPEN]
SuperStrict
Framework BRL.StandardIO

Type FType
   Field mem:Byte Ptr
   Field par:FType
   Field i:Int
   
   Method New()
      ii:+1
      mem = MemAlloc(1200)
      i=ii
   EndMethod
   
   Method Delete()
      Print "delete():"+i
   EndMethod
   
   Function Remove(t:Ftype Var)
      t.par=Null
      t=Null
   EndFunction
   
   Global ii:Int=0
EndType

Print GCMemAlloced()

Local t1:Ftype=New FType
Local t2:Ftype=New FType
Local t3:Ftype=New FType
Local t4:Ftype=New FType
t1.par=t2
t2.par=t1
t3.par=t4
t3.par=t1

Print GCMemAlloced()
GCCollect()
Print GCMemAlloced()

FType.Remove(t1)
FType.Remove(t2)
FType.Remove(t3)
FType.Remove(t4)

GCCollect()
Print GCMemAlloced()


Hier ist ein Type.
Ein ganz normaler eigentlich.
Wenn man jetzt das par-Field leer lässt, wird alles ordnungsgemäß Getrashed, ansonsten tut sich nix und ein Leak entsteht.
Nur t3 wird gelöscht, da auf ihn kein par-Field zeigt.

Was soll ich tun?
Was mache ich falsch?

Auf ein "par"-Field kann ich nicht verzichten und ich würde den Dreck entlich gelöst bekommen....
Es muss doch irgendwie gehen.


Wenn es ein Fehler meinerseits ist, oder sogar ein Denkfehler dann weist mich bitte daraufhin Sad



Gruß,
FOODy




PS: Tut mir leid wenn der Post jetzt irgendwie komisch geschrieben ist oder so, aber ich bin halb am Pennen und total verärgert Very Happy
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

Jolinah

BeitragMi, Feb 14, 2007 1:15
Antworten mit Zitat
Benutzer-Profile anzeigen
Beim kurzen drüber blicken ist mir nur folgendes aufgefallen:

Code: [AUSKLAPPEN]
mem = MemAlloc(1200)


Speicher der auf diese Weise manuell reserviert wird, muss auch wieder manuell freigegeben werden. Der Garbagecollector wird ihn nicht aufräumen.

Weiterhin geben Garbagecollectors den Speicher meist nicht sofort wieder frei, sie warten bis eine bestimmte Menge an Speicher reserviert wurde und räumen erst danach auf, damit dieser Prozess nicht andauernd durchgeführt wird.

Das heisst es kann gut sein, dass deine Type-Instanzen noch nicht entsorgt werden, auch wenn keine Referenz mehr darauf zeigt. Die werden dann evtl. erst entsorgt wenn durch neue Objekte noch mehr Speicher reserviert wird oder wenn eine bestimmte Zeitfrist abläuft.

Du kannst das zur Laufzeit testen indem du laufend neue Objekte erstellst und wieder auf Null setzt. Der Speicherverbrauch steigt auf ein gewisses Niveau an und stabilisiert sich dann, statt wie vielleicht erwartet praktisch 0 zu sein.

FOODy

BeitragMi, Feb 14, 2007 10:11
Antworten mit Zitat
Benutzer-Profile anzeigen
Ja so dachte ich es auch erst.
Aber wenn man jetzt die Zeilen, wo die par-Fields gesetzt werden auskommentiert werden _alle_ Instanzen entfernt.
Das man Allokierten Speicher manuell freigeben muss ist klar.
Aber das bringt wenig, wenn die Instanz nicht entfernt wird.
(Da ich z.b. den Speicher im Destruktur freigeben würde)


Gruß,
FOODy


EDIT:
Hier z.b., werden alle Instanzen entfernt, da das par-Field nicht gesetzt ist.
Wenn man die Zeilen aber mit Builded wird nur t3 entfernt, da auf die kein par-Field zeigt.

Code: [AUSKLAPPEN]
SuperStrict
Framework BRL.StandardIO

Type FType
   Field mem:Byte Ptr
   Field par:FType
   Field i:Int
   
   Method New()
      ii:+1
      mem = MemAlloc(1200)
      i=ii
      Print "new():"+i
   EndMethod
   
   Method Delete()
      Print "delete():"+i
      MemFree mem
   EndMethod
   
   Function Remove(t:Ftype Var)
      t.par=Null
   t=Null
      EndFunction
   
   Global ii:Int=0
EndType

Print GCMemAlloced()

Local t1:FType=New FType
Local t2:FType=New FType
Local t3:FType=New FType
Local t4:FType=New FType

Local t5:FType=t3

't1.par=t2
't2.par=t1
't3.par=t4
't3.par=t1

Print GCMemAlloced()
GCCollect()
Print GCMemAlloced()

t5=Null
FType.Remove(t1)
FType.Remove(t2)
FType.Remove(t3)
FType.Remove(t4)

GCCollect()
Print GCMemAlloced()
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

BtbN

BeitragMi, Feb 14, 2007 12:52
Antworten mit Zitat
Benutzer-Profile anzeigen
Öhm, der GC arbeitet via Reference Couting.
Und solang du nicht ALLE Referenzen auf deinen Type entfernst(Das muss nicht heißen auf NULL gesetzt haben), bleibt das auch. Und das handle auf eine Referenz an eine funktion zu übergeben, und dort die LOKALEN Referenz auf Null zu setzen ringt effektiv nix. Denn deine Gloablen referenzen bleiben erhalten, und werden logischerweise nicht gelöscht.


Achja: Var dürfte bei types nicht funkitonieren. (Edit: Schwachsinn, offenbar gehts doch.)


Edit 2: So, habe den fehler. Der grund dafür, dass es ohne die parents geht, und dass es mit parents ofenbar nicht geht ist: Du hast da ne kleine aber feine zyklische Struktur drinne: t1 hat als paerent t2, und t2 hat als parent t1. Das ist 1. Schwachsinn, dass sich 2 Objekte gegenseitig als Parent haben und 2. führt es dazu, dass es immer eine referenz auf t1 und t2 geben wird, und sie deshalb beide nicht gelöscht werden.(par in t1 zeigt auf t2 -> t2 wird nicht gelöscht ;; par in t2 zeigt auf t1 -> t1 wird nicht gelöscht)

Hoffe, das war verständlich.

FOODy

BeitragMi, Feb 14, 2007 13:25
Antworten mit Zitat
Benutzer-Profile anzeigen
@BORNtobeNAMELESS:
Das war beabsichtigt, mit den gegenseitigen Parents.
In diesem Beispiel ist das natürlich schwachsinn.

Aber wenn man z.b. eine Map, List oder Array hat von den "Kinder"-Types und die Kind-Types ein Field haben, dass auf den Parent zeigt, ist es nicht unbedingt schwachsinnig, das gegenseitige Referenzieren.

Oder ein anderes Beispiel.
In BRL.LinkedList zeigen sich die TLink-Typen ja auch gegenseitig an.
Zitat:
Type TLink

Field _value:Object
Field _succ:TLink,_pred:TLink
[...]
EndType


@Var:
Bei Var wird nicht die Referenz der Instanz vergeben, sondern die der Variable.




Gruß,
FOODy
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

BtbN

BeitragMi, Feb 14, 2007 13:28
Antworten mit Zitat
Benutzer-Profile anzeigen
Meld dich mal bei ICQ irgendwie, hab da noch was zu gefunden, was den so besser gehen würde.

Markus2

BeitragMi, Feb 14, 2007 13:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Statt =New FType
kannst du eine Funktion nehmen die diese Struktur in eine Liste speichert .
Und wenn du was entfernen willst gehst du die Liste durch und
setzt da alle passenden Einträge auf NULL .

Local Bla:FType=FType.Neu()

function Neu:FType()
local x:FType=new FType
liste.addlast x
return x
end function


die Idee mit Remove ist gut aber nicht korrekt

Function Remove(t:Ftype Var)
if t.par<>null then Ftype.Remove t.par
t=Null
End Function

FOODy

BeitragMi, Feb 14, 2007 14:11
Antworten mit Zitat
Benutzer-Profile anzeigen
@BORNtobeNAMELESS:
Falls du mich meinst, kann ich nur sagen das ich im moment auf der Arbeit bin ^^

@Markus2:
Man kann die Instanz in ein Array speichern.
Man kann die Instanz in einer Map speichern.
UND, was hier anscheiend als unwichtig geltend gemacht wird, man kann die Instanz auch in einer normalen Variable speichern.

Es dürfte dem GC eigentlich Esel sein, welche Speichermethode ich jetzt wähle.
Die Instanz wird sowieso entweder in einem Field oder in einer Variable gespeichert.

@Remove:
Das würde ich so nicht sagen.
Es müssten normalerweise nach dem entfernen einer Instanz auch automatisch alle Felder "entfernt/Nulliert" werden.
Da würd ich sogar behaupt, dass ein t.par=null u.ä.s. überflüssig sind.
Mal davon abgesehen das bei einer gegenseitigen Referenzierung wie in meinem Beispiel, die Funktion endlos-oft aufgerufen wird Wink


Gruß,
FOODy


PS: Falls ich etwas falsch verstanden habe, bitte ich um eine Aufklärung.
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

Suco-X

Betreff: ....

BeitragMi, Feb 14, 2007 18:09
Antworten mit Zitat
Benutzer-Profile anzeigen
Hi
Das ist kein Leak, dass ist einfach der GC. Die genaue Logik von ihm kenne ich nicht, er sieht es aber scheinbar noch nicht für nötig, deine Daten zu entfernen, auch wenn du sie nicht mehr brauchst.
Habe mal einen zweiten Test gemacht:

Code: [AUSKLAPPEN]

SuperStrict
Framework BRL.StandardIO

Type FType
   Field mem:Byte Ptr
   Field par:FType
   Field i:Int
   
   Method New()
      ii:+1
      mem = MemAlloc(1200)
      i=ii
   EndMethod
   
   Method Delete()
      Print "delete():"+i
   EndMethod
   
   Function Remove(t:Ftype Var)
      t.par=Null
      t=Null
   EndFunction
   
   Global ii:Int=0
EndType



DerTest()

Print "DANACH"
GCCollect()
Print GCMemAlloced()



Function DerTest()
   Print "DER TEST"
   Print GCMemAlloced()
   
   Local t1:Ftype=New FType
   Local t2:Ftype=New FType
   Local t3:Ftype=New FType
   Local t4:Ftype=New FType
   t1.par=t2
   t2.par=t1
   t3.par=t4
   t3.par=t1
   
   Print GCMemAlloced()
   GCCollect()
   Print GCMemAlloced()
   
   FType.Remove(t1)
   FType.Remove(t2)
   FType.Remove(t3)
   FType.Remove(t4)
   
   GCCollect()
   Print GCMemAlloced()
End Function




Wie du siehst, räumt er in der Funktion noch nicht auf. Aber danach räumt er alles aus dem Speicher, da es dann erst nötig wird.
Du kannst dir also sicher sein, dass der GC die Daten garantiert so verwaltet, dass der Speicher immer Clean bleibt. Wenn du nicht zufällig alles mit Pointern und Speicherbereichen zukleisterst, brauchst du dir also über Leaks keine Sorgen zu machen.
Mfg
Intel Core 2 Quad Q8300, 4× 2500 MHz, 4096 MB DDR2-Ram, GeForce 9600GT 512 MB

FOODy

BeitragDo, Feb 15, 2007 10:35
Antworten mit Zitat
Benutzer-Profile anzeigen
@Suco-X:
Hmmm...
Finde es aber dennoch etwas seltsam warum er mit einem par anders reagiert als ohne :S

Werd mich dann wieder melden, wenn ich in diesem Bereich wieder ein Problem bekomme. :/

Danke, für eure Hilfe! Very Happy


Gruß,
FOODy
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB
 

Dreamora

BeitragDo, Feb 15, 2007 10:49
Antworten mit Zitat
Benutzer-Profile anzeigen
Er reagiert nicht anders, sondern genau gleich.

Aber wie oben geschrieben: Solange RefCount > 0 wird nicht aufgeräumt.
BM hat kein Root Reference Count um zu sehen ob du überhaupt noch auf die Variable zugreifen kannst. Du musst via Remove Methoden selbst dafür sorgen das alle Referenzen aufgelöst werden bevor du das objekt "nullst"


Ach ja, dein hauptmem leak ist trotzdem noch drin.

Solange da in Method Delete() kein memfree self.mem steht gibts nen Leak.
Nur wirst du den verpassen, denn GCMemAlloced gibt nur den von BM managten Speicher aus!!
Das schliesst memalloc als auch soundfiles explizit aus (da sie über C Memory Funktionen laufen), deren speicherverbrauch bekommst du dann im prozessmanager entgegengeschleudert Smile
Ihr findet die aktuellen Projekte unter Gayasoft und könnt mich unter @gayasoft auf Twitter erreichen.

FOODy

BeitragDo, Feb 15, 2007 11:09
Antworten mit Zitat
Benutzer-Profile anzeigen
@Dreamora:
Beim Zweiten Code hatte ich nen MemFree "eingebaut", aber das ist ja nicht das Problem, welches ich habe, da ich MemAlloc sowieso fast nie verwende.

Mein Problem ist nur, das ich einen Styleparser habe für meine Gui, und wenn man jedesmal einen neuen Style läd, immer mehr Speicher reserviert wird, obwohl ich im Destruktor alles vernichte. (Ich verwende weder Pointer noch MemAlloc)


Kann es eigentlich auch sein, das wenn man z.b. in einer Map Instanzen hat, und die Map zerstört, dass dann dennoch die Instanzen am "leben" bleiben?
Weil bei mir die Instanzen erst freigegeben wurden als ich vor dem Leeren und vernichten der Map, die Instanzen durchgegangen bin und einzeln "Methodenmäßig" gelöscht habe.
Könnte mich aber auch Irren :S
Oder die wären sogar erst später gelöscht worden, als wenn ich das direkt mache.



Gruß,
FOODy
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

Suco-X

Betreff: .....

BeitragDo, Feb 15, 2007 14:00
Antworten mit Zitat
Benutzer-Profile anzeigen
Hi Foody
Normalerweise solltest du eine Map/Liste einfach zerstören können, ohne die darin enthaltenen Distanzen Manuell zu nullen. Vorrausgesetzt, diese Instanzen zeigen nicht noch irgendwo anders hin.
Das ganze kann man ja wieder mit einem Test nachprüfen:

Code: [AUSKLAPPEN]

Strict

Type TStyle
   Field X:Int, Y:Int
   Field i:Int
   
   Global Index:Int
   
   Method New()
      Index:+1
      i = Index
   End Method
   
   Method Delete()
      Print I+": "+"Delete"
   End Method
   
End Type


Wush()

GCCollect()

Function Wush()
   Local Map:TMap = New TMap
   For Local i:Int= 0 Until 1000
      Map.Insert(String(i), New TStyle)
   Next
End Function


Geht alles weg. Zu deinem anderen Problem. Schau dir mal dieses Beispiel hier an

Code: [AUSKLAPPEN]

Strict

Type TStyle
   Field X:Int, Y:Int
   Field i:Int
   
   Global Index:Int
   
   Method New()
      Index:+1
      i = Index
   End Method
   
   Method Delete()
      Print I+": "+"Delete"
   End Method
   
End Type


Graphics 800,600,0


Repeat
   Cls
   Local Style:TStyle

   Style = New TStyle
   
   
   DrawText GCMemAlloced(),10,10
   
   
   Flip
Until KeyHit(KEY_ESCAPE)


Wie du siehst, räumt er immer nur stückchenweise den Speicher auf, wenn er zu voll wird. Sicher, dass dies nicht der Fall ist, den du als Fehler ansiehst?
Mfg
Intel Core 2 Quad Q8300, 4× 2500 MHz, 4096 MB DDR2-Ram, GeForce 9600GT 512 MB

FOODy

BeitragDo, Feb 15, 2007 14:11
Antworten mit Zitat
Benutzer-Profile anzeigen
@Suco-X:
Hmmm...
Das ist gut, dass es geht.
Dann werde ich wohl einen Fehler im Code übersehen haben, denn der Allokierte Speicher steigt jedesmal wenn ich einen Style "überlade" (alten Löschen, neuen Laden) (Als "ob" der Alte noch da wäre (also nicht gelöscht wurde)).
Werde mal, wenn ich Zeit habe einen kleinen DebugCounter machen bei den Types, um zu schauen wo was nicht gelöscht wird. ( Toll, das mir diese Idee erst jetzt an den Kopf geprallt ist >_> )

Danke, Suco-X. Very Happy


Gruß,
FOODy
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

FOODy

BeitragFr, März 23, 2007 17:49
Antworten mit Zitat
Benutzer-Profile anzeigen
Irgendwie check ich mal wieder was nicht.
Wieso werden hier nicht alle Instanzen zerstört?

Code: [AUSKLAPPEN]
SuperStrict
Import BRL.GLMax2D

Type TType
   Field p:TType
   Field n:Int

   Method New()
      total:+1
      n=total
      list.addlast Self
   EndMethod
   
   Method Delete()
      Print n
      Destroy()
      total:-1
   EndMethod

   Method Destroy()
      p=Null
   EndMethod

   Function Clear()
      For Local t:TType=EachIn list
         t.Destroy()
      Next
      list.clear()
   EndFunction

   Global list:TList=New TList
   Global total:Int
EndType

Graphics 480,320

(New TType).p = New TType
New TType
New TType
New TType
Local t1:TType = New TType
Local t2:TType = New TType
t1.n = 500
t2.n = 600
t1.p = t2

Repeat
   Cls
      If MouseHit(MOUSE_RIGHT)
         t1=Null
         t2=Null
         TType.Clear() '.list
      EndIf
      DrawText TType.total,5,5
   Flip
   GCCollect()
Until KeyDown(KEY_ESCAPE)


Blick da nicht irgendwie rüber warum das nicht geht........
Wär mal nen schönes Feature wenn man auch manuell Objekte Zerstören könnte -.-

Weiß da jemand woran das liegt?
Ist bestimmt wieder ein denkfehler meinerseits...



Gruß,
FOODy


PS:
Und warum wird bei diesem Beispiel nur ein Objekt zerstört?! (Bei welchem kein SetN eingesetzt wurde)

Code: [AUSKLAPPEN]
SuperStrict
Import BRL.GLMax2D

Type TType
   Field p:TType
   Field n:Int

   Method New()
      total:+1
      n=total
      list.addlast Self
   EndMethod
   
   Method Delete()
      Print n
      Destroy()
      total:-1
   EndMethod

   Method SetN:TType(n:Int)
      self.n=n
      Return Self
   EndMethod

   Method Destroy()
      p=Null
   EndMethod

   Function Clear()
      For Local t:TType=EachIn list
         t.Destroy()
      Next
      list.clear()
   EndFunction

   Global list:TList=New TList
   Global total:Int
EndType

Graphics 480,320

(New TType).SetN(1).p = (New TType).SetN(2)
(New TType).SetN(3)
(New TType).SetN(4)
(New TType).SetN(5)
Local t1:TType = New TType
Local t2:TType = New TType
t1.n = 500
t2.n = 600
t1.p = t2

Repeat
   Cls
      If MouseHit(MOUSE_RIGHT)
         t1=Null
         t2=Null
         TType.Clear() '.list
      EndIf
      DrawText TType.total,5,5
   Flip
   GCCollect()
Until KeyDown(KEY_ESCAPE)


Kann doch nicht dran liegen das SetN ne Referenz der instanz zurückgibt -_-"
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

FOODy

BeitragMo, März 26, 2007 10:20
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich bekomm das einfach nicht hin :/
Habe mal das "Problem" hochgeladen.
Wenn man bei der main.bmx jetzt die definierungen von class und frame auskommentiert (nicht die deklarierung sondern nur die wertzuweisung) werden alle FStyleClass und FStyleFrame Instanzen Zerstört ansonsten bleibt eine Class und eine Frame Instanz übrig. (obwohl ich vor der Löschung beide Variablen noch auf Null gesetzt habe)

Download: http://www.onkel-foody.de/Daten/FStyle.rar


Ich hoffe ihr könnt mir helfen...

Gruß,
FOODy
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB
 

Dreamora

BeitragMo, März 26, 2007 18:46
Antworten mit Zitat
Benutzer-Profile anzeigen
Hab mir das hochgeladene net angesehen nur den code oben.
Da verwendest du auf Main Scope ebene Local ... die werden immer bestehen bleiben, denn locals haben, wenn der Scope nicht endet, kein definiertes ende. Will heissen wenn du sie auf "Mainloop" Ebene nutzt werden sie erst mit der zerstörung des Programmes auch aufgehoben, vorher nicht, da ein = NULL sie während des laufenden Scopes nur bedingt interessiert.

Auf Hauptprogrammebene alles global definieren oder garnicht definieren und direkt eine Managerklasse nehmen.
Ihr findet die aktuellen Projekte unter Gayasoft und könnt mich unter @gayasoft auf Twitter erreichen.

FOODy

BeitragMo, März 26, 2007 19:35
Antworten mit Zitat
Benutzer-Profile anzeigen
@Dreamora:
Das ergibt irgendwie Sinn und scheint auch logisch zu sein.
Danke, Dreamora!

Bei weiteren Unklarheiten werd ich mich wieder melden Wink
Danke nochmal an alle hier, die sich Zeit genommen haben mir zu helfen Smile


Gruß,
FOODy
BlitzMax + MaxGUI, 64-bit Arch Linux: Intel Core² E8500 | 8 GB Ram | GeForce GTX 460 · 1024 MB

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Beginners-Corner

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group