Seite 1 von 5

Speicherleck bei Nutzung von Strukturen(mitStrings)im Memory

Verfasst: 23.01.2006 14:29
von Toshy
Hallo.

Auf das Problem bin ich gestoßen im Beitrag
http://forums.purebasic.com/german/view ... 3593#73593

Ich wollte muß Strukturen (auch mitPointer) in den Memory schreiben und NicTheQuick hat mir da echt geholfen. Ich bin da aber auf ein Problem gestoßen. Löscht man diese bleibt anscheinend zwangsweise der Inhalt des Pointerziels (ein Sting aus der Struktur) im Speicher zurück.
Vermutlich ist das Freigeben von Speicher einer Struktur nicht angedacht bzw. bei überschreiben der selben Struktur (im exakt selben speicherbereich) wird der alte gelöscht (hoffe ich mal).
wie kann man das aber machenn, wenn man den strukturpointer ständig wechselt um im Memory zu schreiben.

Beispielcode und meinen Text findet ihr unter dem Link oben oder hier nochmal:

Mir ist da nochwas aufgefallen,leider wie alles nur im Kopf, da der Code noch nicht ausführbar ist den ich nutze.

Wenn ich nun per stuktur einen speicherplatz (mit ...memory) erstelle, dort dann elemente reinschreibe, so wie ihn deinem code oben, dann können ja auch pointer vorkommen. das ist ansich kein problem.
auch muß ich ja nicht den inhalt kopieren, weil ich die copymemory funktion nur benutze um die daten temporär auszulagen damit ich den "originalspeicher" in der größe ändern kann.
aber in diesem fall des hin und her kopierens, wie auch in "deinem einfachen fall" mit "freememory" wird "mindestens der ram" der allociert wurde freigegeben.
wie aber verhält es sich mit pointern? woher weiß pb wo und wann ein pointer gelöscht wird oder nicht.

wird also wenn ich die daten (pointer) kopiert habe und das original lösche dort auch der inhalt des zieles der pointer gelöscht oder nur der pointer?

ich befürchte, das der inhalt nicht gelöscht wird. das wäre zwar einerzeit gar nicht mal schlecht (sonst wäre ja wohl auch die kopie weg, da der pointer auf die selbe stelle zeigt), aber sobald ich reallocate oder freememory nutze würden die inhalte die durch pointer markiert sind im speicher bleiben und ich hätte ein leck.

weißt du wie das genau aussieht und wie könnte ich das umgehen?
wie kann man also z.b. in bei deinem bespiel die strings komplett aus dem speicher bekommen damit kein leck entsteht. (falls da eines entstehen würde).

mir depp fällt das leider erst jetzt auf, wo ich die komplette ramdatenverarbeitung schon im grunde fertig habe. shit.

[edit]
Code:

OpenConsole()
Structure TestStruc
l.l
s.s
f.f
EndStructure
;*pElement.TestStruc = *pArray ;Pointer zum Speichern dem ersten Element bergeben

For i = 1 To 100000
Elemente.l = 10 ;Anzahl der Element
*pArray = AllocateMemory(SizeOf(TestStruc) * Elemente) ;Speicher resevieren fr alle Elemente
*pElement.TestStruc = *pArray ;Pointer zum Speichern dem ersten Element bergeben
;Print(Str(i))
For a.l = 1 To Elemente ;Elemente fllen
*pElement\l = Elemente + 1 - a
*pElement\s = "Zahl: " + Str(a) + "-------------------------------------------------------------"
*pElement\f = 1 / a
*pElement + SizeOf(TestStruc)
Next

*pElement = *pArray ;Erstes Element wieder auswhlen
For a.l = 1 To Elemente ;Elemente auslesen
;Debug Str(a) + ". Element:"
;Debug *pElement\l
;Debug *pElement\s
;Debug *pElement\f
;Debug ""
;*pElement + SizeOf(TestStruc)
Next
FreeMemory(*pArray) ;Array wieder freigeben
Next i

For i = 1 To 100
Delay(100)
Next i
CloseConsole()


Der Code hier bringt wirklich ein "Speicherleck". Er sollte ja wenn möglich den Speicher nach jeden Durchlauf freigeben, aber es bleibt etwas übrig und so sind bei dem durchlauf hier ca. 40 MB Ram weg.

Bin für Tips dankbar.

Danke
Thorsten
Das soll kein Crossposting sein, aber da es eigendlich in den alten Beitrag gehört, aber ein neues Problem ist öffne ich diesen Beitrag neu. Sonst lies es logischer weise kaum jemand.

Verfasst: 23.01.2006 15:12
von Konne
Also du kannst Strings Structuren usw mit freememory(*Pointer) freeen.

Verfasst: 23.01.2006 15:21
von Toshy
das hatte ich testweise schon versucht. klappte nicht.

in dem beispiel was ich "bekommen und zum testen angepaßt" habe muß ich ja nur den pointer von *pElement nehmen und 4 dazurechner (wegen dem long davor) und dann müßte ich den string pointer haben. aber mit diesem wurde der string nicht gelöscht. könnte zwar einen fehler gemacht haben, aber ich glaube nicht.

diesen "umweg" müßte ich notfalls hinnehmen, aber gibt es keine andere möglichkeit? ich wollte ja gerade durch nutzen von memory geschwindigkeit gewinnen. die würde mir hier krass verloren gehen. und dann ist nicht abzusehen wo und wie sich täglich die position oder menge der pointer pro struktur ändern.

aber wie gesagt, das wichtigste ist, das freememory bei meinen versuchen nicht funktioniert. vielleicht nur ein tipfehler, aber ich habe die angst, dem ist n icht so ;-)

Verfasst: 23.01.2006 15:30
von ts-soft
Die Speicherfunktionen von PB kennzeichnen freigegebenen Speicher, nur als diesen und überlassen es dann Windows, diesen tatsächlich freizugeben. So hab ichs jedenfalls ungefähr im englischem Forum gelesen.
Meistens wird der Speicher also erst beim beenden des Programmes dann tatsächlich freigeben.
Ist wohl so ähnlich, wie mit DLLs, die auch nicht sofort, wenn die letzte Instanz geschlossen wurde, aus dem Speicher entfernt wird.

Für die Richtigkeit meiner Aussagen möchte ich aber nicht die Hand ins Feuer legen :wink:

Verfasst: 23.01.2006 15:42
von PMV
Das ist mir auch schon aufgefallen ... da konnte mir aber keiner helfen und keiner wusste so recht warum

Aber wenn PureBasic die Pointer nicht verwalten kann, dann musst du das selber machen. Das sollte nicht all zu schwer sein :D .

Code: Alles auswählen

OpenConsole() 
Structure TestStruc 
l.l 
*s
f.f 
EndStructure 
;*pElement.TestStruc = *pArray ;Pointer zum Speichern dem ersten Element bergeben 

For i = 1 To 100000 
  Elemente.l = 10 ;Anzahl der Element 
  *pArray = AllocateMemory(SizeOf(TestStruc) * Elemente) ;Speicher resevieren fr alle Elemente 
  *pElement.TestStruc = *pArray ;Pointer zum Speichern dem ersten Element bergeben 
  ;Print(Str(i)) 
  For a.l = 1 To Elemente ;Elemente fllen 
    *pElement\l = Elemente + 1 - a 
    Text$ = "Zahl: " + Str(a) + "-------------------------------------------------------------" 
    *pElement\s = AllocateMemory(Len(Text$)+1)
    PokeS(*pElement\s, Text$)
    *pElement\f = 1 / a 
    *pElement + SizeOf(TestStruc) 
  Next 

  *pElement = *pArray ;Erstes Element wieder auswhlen 
  For a.l = 1 To Elemente ;Elemente auslesen 
    FreeMemory(*pElement\s)
    ;Debug Str(a) + ". Element:" 
    ;Debug *pElement\l 
    ;Debug PeekS(*pElement\s) 
    ;Debug *pElement\f 
    ;Debug "" 
    *pElement + SizeOf(TestStruc) 
  Next 
  FreeMemory(*pArray) ;Array wieder freigeben 
Next i 

For i = 1 To 100 
Delay(100) 
Next i 
CloseConsole()
MFG PMV

Verfasst: 23.01.2006 15:43
von Toshy
Da hoffe ich mal auf weitere Infos, aber ich kann mir nicht so ganz vorstellen das das so ist. Denn erstens werden ja die anderen Speicherfunktionen (besonders ...memory) korrekt und sofort freigegeben und wenn dem nicht so wäre, dann würden bei spielen programmen ja hunderte megabyte an speicher in wenigen sekunden verloren gehen.
wenn dazu aber jemand mehr weiß, ich höre.

[edit]
@PMV
unser nachrichten haben sich überschnitten, ich schaue mir deinen code nach dem mittag an, oder gleich ;-)

Verfasst: 23.01.2006 15:47
von Konne
Nein es ist warscheinlich so dass Windows den Speicher immer erst dann freigibt, wenn er voll ist. Also kennzeichnet windows speicher als wiederverfügbar. Daher wirst du jedenfalls bei stehen hinter dem alloketierten Memory auch nicht immer Nullen. Naja sdo denke ich mir das jedenfalls.

Verfasst: 23.01.2006 15:50
von ts-soft
Friedrichs hat geschrieben:und wenn dem nicht so wäre, dann würden bei spielen programmen ja hunderte megabyte an speicher in wenigen sekunden verloren gehen.
Das siehste dann verkehrt, weil dann besteht ja Bedarf, den Speicher freizugeben, so das Windows das dann auch erledigt.
Wenn ich mit PB speicher reserviere und anschließend freigebe, diese per API prüfe, mit GetSize_(), erhalte ich die ursprünglich reservierte Grösse zurück.

Verfasst: 23.01.2006 16:28
von PMV
Ich hab damit jetzt ein wenig rumgespielt ...

in Worten ging das so:
- Speicherreservierung von 1 GB mittels AllocateMemory() klapt wunder bar

- Den Code aus dem ersten Post ausführen lassen (das 10 Fache),
speicher bleibt laut Windows von 1,5 GB resserviert

- Speicherreservierung von 1 GB mittels AllocateMemory() klapt nicht!
- Speicherreservierung von 100 MB mittels AllocateMemory()

ich hab den Code jetzt nur schnell mal eben zusammengeschustert ^_^
#Size auf die eigentliche Ramgröße stellen oder mehr
Den Schleifendurchlauf für die Erstellung der Elemente mit den Strings so
anpassen, das er am besten den kompleten Ram belegen müsste.
Das ganze ist zumindest so gedacht, viel ahnung von
Speicherreservierung hab ich ja nicht -.-, daher kann meine Denkweise
natürlich auch falsch sein ...

Code: Alles auswählen

#Size = 1000000000

OpenConsole() 

PrintN("Speicherreservierung ...")

For i = 1 To 10
  Timer = ElapsedMilliseconds()
  *Memory = AllocateMemory(#Size)
  If *Memory
    PrintN(Str(i) + ". Mal in " + Str(ElapsedMilliseconds() - Timer) + " ms")
    FreeMemory(*Memory)
  Else
    PrintN("Fehler")
    Input()
    End
  EndIf
Next

PrintN("")
PrintN("")
Print("Pointerproblem anwenden ... ")

Structure TestStruc 
l.l 
s.s
f.f 
EndStructure 
;*pElement.TestStruc = *pArray ;Pointer zum Speichern dem ersten Element bergeben 

For i = 1 To 1000000
  Elemente.l = 10 ;Anzahl der Element 
  *pArray = AllocateMemory(SizeOf(TestStruc) * Elemente) ;Speicher resevieren fr alle Elemente 
  *pElement.TestStruc = *pArray ;Pointer zum Speichern dem ersten Element bergeben 
  For a.l = 1 To Elemente ;Elemente füllen 
    *pElement\l = Elemente + 1 - a 
    *pElement\s = "Zahl: " + Str(a) + "-------------------------------------------------------------" 
    *pElement\f = 1 / a 
    *pElement + SizeOf(TestStruc) 
  Next 

  FreeMemory(*pArray) ;Array wieder freigeben 
Next i 

PrintN("fertig")
PrintN("")
Print("Speicherreservierung (" + Str(#Size) + " Byte) ... ")
Timer = ElapsedMilliseconds()
WTimer = Timer
*Memory = AllocateMemory(#Size)
i = 1
Size = 2
While *Memory = #False And Size > 1
  i * 10
  Size = #Size / i
  PrintN("Fehler")
  Print("Speicherreservierung (" + Str(Size) + " Byte) ... ")
  Timer = ElapsedMilliseconds()
  *Memory = AllocateMemory(Size)
Wend
If *Memory
  FreeMemory(*Memory)
  PrintN("in " + Str(ElapsedMilliseconds() - Timer) + " ms")
EndIf
Input()

CloseConsole()
Wen Windows den Speicher markiert und ihn wieder komplet freigeben
würde, wenn er gebraucht wird, dann müsste die Reservierung am Ende
von 1 GB doch klappen, oder?

MFG PMV

Verfasst: 23.01.2006 17:09
von helpy
Hi all,

Code: Alles auswählen

#TEST_LOOP = 1024*1024

Structure TEST_STRUCT
	s.s
EndStructure

Procedure TestStruct()
	Protected test.TEST_STRUCT
	test\s = "test"
EndProcedure
		
For i = 1 To #TEST_LOOP
	TestStruct()
Next i

MessageRequester("TEST","Memory Leak TEST")
Probiert mal das! Wenn die Meldung (MessageRequester) erscheint, dann im Taskmanager schauen ... dort werden bei mir ca. 2,5MByte angezeigt!

Mit folgendem Test ...

Code: Alles auswählen

#TEST_LOOP = 1024*1024

Structure TEST_STRUCT
	s.s
EndStructure

For i = 1 To #TEST_LOOP
	*test.TEST_STRUCT = AllocateMemory(SizeOf(TEST_STRUCT))
	If *test <> 0
		*test\s = "Ein Test"
		FreeMemory(*test)
	EndIf
Next i

MessageRequester("TEST","Memory Leak TEST")
... werden dagegen etwas über 18MByte angezeigt.

Fazit:
Bei Strukturen, die ganz normal angelegt werden (erster Test), werden auch die Strings ordnungsgemäß freigegeben. Das sieht am auch am ASM-Code, denn dort findet sich eine Funktion "_SYS_FreeStructureStrings@8".

Beim zweiten Test dagegen weiß ja PureBasic nicht, dass eine Struktur freigegeben wird, denn FreeMemory gibt einfach nur den allokierten Speicher frei, und allokiert wurde ein Speicherbereich in Größe der Struktur ... und da ist aber nur der Pointer auf den String enthalten und nicht der String selbst. Und im ASM-Code dieses Beispiels wird auch nicht die Funktion "_SYS_FreeStructureStrings@8" aufgerufen.

Der Speicher von Strings, die über Strukturen angesprochen werden und die dynamisch mit AllocateMemory angelegt wurden, werden erst nach ENDE des Programms freigegeben!

Lösung: Wer sich mit Inline ASM und dem Aufruf von PB internen Funktion auskennt, könnte vor dem Freigeben des dyn. allokierten Speichers die Funktion "_SYS_FreeStructureStrings@8" aufrufen, um die darin enthaltenen Strings freizugeben.

cu, helpy