Speicherleck bei Nutzung von Strukturen(mitStrings)im Memory

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
helpy
Beiträge: 636
Registriert: 29.08.2004 13:29

Beitrag von helpy »

Halli hallo, ...

1. Speicherbereiche, die mit AllocateMemory() reserviert werden, werden meines Wissens nach immer mit dem Wert "0" initialisiert!
Steht jetzt nicht beim Hilfetext von AllocateMemory(), deshalb bitte ich die Koriphäen von PB dieses entsprechend zu bestätigen, oder zu sagen, das ist nicht so!

2. Strings innerhalb von Strukturen werden in der Struktur immer durch einen Pointer auf den String abgebildet.

Code: Alles auswählen

Structure TwoStrings
    s1.s
    s2.s
EndStructure

Debug(SizeOf(TwoStrings)) ; ... ergibt 8
Solange den String-Elementen einer Struktur-Variablen noch kein String zugewiesen wurde, existiert auch noch kein zugehöriger String in der String-Verwaltung von PureBasic! Erst wenn ein String zugewiesen wird, wird durch die String-Verwaltung von PB physikalischer Speicher für den String allokiert. Die Größe dieses Speichers entspricht (nach meinen Tests) einem Vielfachen von 8Byte. D.h. ein Null-String braucht mindestens 8Byte PLUS den Overhead für die String-Verwaltung, wobei ich nicht weiß, wieviel PB für die Verwaltung eines Strings zusätzlich benötigt.

3. Der Befehl FreeMemory jedoch weiß nicht, wofür der allokierte Speicher verwendet wurde! Deshalb, ist dieser Funktion auch nicht bekannt, dass in diesem Speicherbereich ein (oder mehrere) Pointer auf einen String liegt, wobei der Speicherbereich des Strings außerhalb des Speicherbereichs für die Struktur liegt.

==> Deshalb muss man in diesem Fall immer selbst aufräumen!

==> Macht man das ganze z.B. mit Arrays, dann muss man das nicht aufräumen:

Code: Alles auswählen

#TEST_SIZE = 1024*1024

Structure StructWithStrings
	s1.s
	x.l
	s2.s
EndStructure

MessageRequester("test","Start of Program")
; =>   2.500 KByte
Dim test.StructWithStrings(#TEST_SIZE)
MessageRequester("test","Dim test.StructWithStrings("+Str(#TEST_SIZE)+")")
; =>   2.524 KByte
For i = 0 To #TEST_SIZE
	test(i)\s1 = "test1"
	test(i)\s2 = "test2"
Next i
MessageRequester("test","Array filled with Strings")
; =>  47.680 ËByte
For i = 0 To #TEST_SIZE
	test(i)\s1 = ""
	test(i)\s2 = ""
Next i
MessageRequester("test","Strings in Array are cleared with NULL string")
; =>  47.680 ËByte
Dim test.StructWithStrings(0)
MessageRequester("test","Array is set to size ZERO!")
; =>   2.660 ËByte
Nachteil: Arrays sind immer global!


@thorsten Friedrichs
Ich hoffe, dass das deine Fragen (wenn ich sie denn richtig verstanden habe) größtenteils beantwortet.

So, das wars für heute,
cu, helpy
Toshy
Beiträge: 713
Registriert: 22.03.2005 00:29
Computerausstattung: Computer und Strom vorhanden
Wohnort: LK Wolfenbüttel

@helpy

Beitrag von Toshy »

Nett von dir gemeint,
aber das hat mit meinem letzten Beitrag nichts zu tun.

Ich arbeite mit allocatememory und nicht mit DIM. DIM kann ich nicht nutzen. Und bei memory geht das nicht so. Vielleicht macht unter umständen Dim auch probleme. Ich habe deinen code mal so angepaßt, das er so oft wie bei meinem bespiel durchläuft (schleife wo anders eingebaut). bei dim scheint das problem nicht aufzutreten, ist aber auch logisch, da pb ja selbst mehrere strukturen anlegt und nicht eine für den ganzen "dimbereich".

und wenn ich jetzt bei allocatememory oder reallocatememory
*element\s = ""
nutze hat das auch keine große wirkung. zwar wird dann der string auf null gesetzt und dadurch dieser speicher wirklich nicht mehr verbraucht und speicher gespart, aber der speicher der für die verwaltung der strukturstrings geht trotzdem verloren. und das sorgt nur dafür, das nicht so viel speicher wegfällt. aber bei mir fällt das trotzdem noch EXTREM ins gewicht.

und bei dir hat das = "" auch keinen sinn, denn der speicher wird bei structurearrays auch ohne dies wieder freigegeben.

das problem mit dem freigeben des speichers ist ja anscheinend gelöst, nur das einen andere angefrage "sicherheitsproblem" ist noch offen.
schön wäre es, wenn fred in pb4 noch eine pb funktion entsprechend dem ASM code einbaut. geht auch so, aber dann hat man asm raus.

gruß
Toshy
Toshy
Beiträge: 713
Registriert: 22.03.2005 00:29
Computerausstattung: Computer und Strom vorhanden
Wohnort: LK Wolfenbüttel

Beitrag von Toshy »

Nachtrag zum vermuteten Problem meines Beitrags vom
24 Jan 2006 18:33:48

Da ich Sorge habe, das ein Fehler auftritt, will ich gerne die mit ASM gelöschten Stringpointer auf NULL setzten. Das m üßte ja mit pokeL(*struktur+offset,0) gehen. natürlich am besten (am ende) der löschprocedure. oder gibt es da ein problem? das geht aber nur bei einzellöschen der stringpointer, bei der verbesserten routine weiß ich mit pbcode ja nicht, an welchen stellen ich löschen muß, außer ich kann das auch selbst in einer schleife aus dem label auslesen.

wenn die routine schon in asm ist, kann man das auch gleich kurz in asm schreiben?

Und dann nochwas. in der procedure
FreeStructuredStrings(ptrstruct.l, ptroffsets.l)
werden longs statt pointern genommen, warum keine pointer?
vermutlich wegen dem MINIS EINE (-1) als endzeichen, aber wenn man longs nimmt, dann wären ja "nur" maximal 2GB ansprechbar. ist das korrekt?

Ich frage zur sicherheit, weil dann baue ich am programmanfang eine überprüfung ein ob der speicher über 2 gb groß ist und dann darf das programm halt nicht starten. oder gibt es bei pointern auch minuswerte? währe aber unlogisch.

Danke Thorsten
Benutzeravatar
helpy
Beiträge: 636
Registriert: 29.08.2004 13:29

Beitrag von helpy »

Guten Morgen Thorsten,

Das mit den Arrays war eigentlich nur ein Beispiel, dass da manuelles aufräumen nicht nötig ist und eine Verdeutlichung des vorhergeschriebenen.

Hier eine Routine zum löschen der String-Pointer innerhalb der Struktur:

Code: Alles auswählen

Procedure ResetStringPointer(ptrstruct.l, ptroffsets.l)
	Protected *p.Long, offset.l
	Restore offTestStruc
	Read offset
	While offset <> -1
		*p = ptrstruct + offset
		*p\l = 0
		Read offset
	Wend
EndProcedure
Aufruf nach FreeStructuredStrings:

Code: Alles auswählen

ResetStringPointer(*pElement, ?offTestStruc)
(im Beispiel http://forums.purebasic.com/german/view ... 3864#73864)

Könnte auch in die Funktion [c]FreeStructuredStrings(ptrstruct.l, ptroffsets.l)[/c] aufgenommen werden.

Das Null-Setzen der String-Pointer hat aber in diesem Fall meines Erachtens nur dann Sinn, wenn die Struktur selbst noch weiterverwendet wird (also der mit AllocateMemory reservierte Speicherplatz nicht freigegeben wird).

In deinem Beispiel aber wird der Speicherbereich für die Struktur ja durch FreeMemory freigegeben und somit steht die Struktur selbst auch nicht mehr zur Verfügung. Da würde das Null-Setzen der String-Pointer nur einen Performance-Verlust bedeuten.

Wenn nun das Programm über eine freigegebene Struktur auf einen nicht-existenten String zugreifen sollte ... ja da würden Probleme entstehen.
Dieses ist aber eher ein Problem der sauberen/logischen Programmierung, denn das sollte gar nicht erst passieren.

cu, helpy
Benutzeravatar
Andre
PureBasic Team
Beiträge: 1765
Registriert: 11.09.2004 16:35
Computerausstattung: MacBook Core2Duo mit MacOS 10.6.8
Lenovo Y50 i7 mit Windows 10
Wohnort: Saxony / Deutscheinsiedel
Kontaktdaten:

Beitrag von Andre »

helpy hat geschrieben:1. Speicherbereiche, die mit AllocateMemory() reserviert werden, werden meines Wissens nach immer mit dem Wert "0" initialisiert!
Steht jetzt nicht beim Hilfetext von AllocateMemory(), deshalb bitte ich die Koriphäen von PB dieses entsprechend zu bestätigen, oder zu sagen, das ist nicht so!
Er wird mit 0 initialisiert - hat mir grade eben fr34k bestätigt.
Habe den Hinweis auch bereits in die Hilfe aufgenommen.
Bye,
...André
(PureBasicTeam::Docs - PureArea.net | Bestellen:: PureBasic | PureVisionXP)
Toshy
Beiträge: 713
Registriert: 22.03.2005 00:29
Computerausstattung: Computer und Strom vorhanden
Wohnort: LK Wolfenbüttel

Beitrag von Toshy »

Aber genau das könnte bzw. wird passieren.

Der Performanceverlust ist schon recht ärgerlich, denn denn wollte ich verhindern. ich achte dabei eigendlich auf jedes Quentchen.
Und mit sauberer Programmierung hat das nur teilweise zu tun.
Ich arbeite auch mit ReAllocatememory, wobei der Speicher schon beschrieben sein (ReAllocatememory ist in vielen fällen schnellr als Allocatememory). Aber auch bei Allocatememory kann ja im Speicher schon was stehen.
Wenn dort was steht und ich überschreibe das mit einer Struktur ist da ja kein Fehler, normal muß ich den Inhalt des zu überschreibenden SPeichers nicht löschen, da er ja mit dem Inhalt der Struktur beschrieben werden soll bzw über die Struktur darauf zugegriffen wird.
Das klappt auch und gibt keine Fehler, auch bei Stringpointern nicht. Nur wie ich vermute und befürchte (so wie du anscheinend auch) wir dadurch, das vorher versucht "werden könnte" einen vorhandenen Longwert als Stringpointer zu interpretieren und dann irgendeinen "Mist" damit macht.

Einzige Mögichkeite ist, vor Zugriff über eine Struktur, den Bereich jedesmal mit "NULLEN" zu überschreiben. Das ist sehr umständlich und vor allem ein großer Performanceverlust. Und ich nutze die "Memoryfunktionen" vor allem wegen dem Geschwindigkeitsvorteil.

Ich hoffe zum "internen Verhalten" kann noch jemand was sagen.

Gruß
Toshy.
ps. Ich antworte momentan vielleicht manchmal einige Zeit nicht, leider kann man das Forum momentan nicht über alle 89.-IP-Adressen erreichen.
Benutzeravatar
helpy
Beiträge: 636
Registriert: 29.08.2004 13:29

Beitrag von helpy »

@Friedrichs, Thorsten, Toshy

Also irgendwie habe ich das Gefühl, dass wir aneinander vorbeireden!
Friedrichs hat geschrieben:Aber auch bei Allocatememory kann ja im Speicher schon was stehen.
NEIN! Eben nicht! Wenn mit AllocateMemory ein Speicherbereich reserviert/allokiert wird, dann steht dort ersteinmal in jedem Byte NULL drinnen. Davon kannst Du nun ja (nach Andre's Rückmeldung) mit Sicherheit ausgehen!

cu, helpy
Benutzeravatar
MVXA
Beiträge: 3823
Registriert: 11.09.2004 00:45
Wohnort: Bremen, Deutschland
Kontaktdaten:

Beitrag von MVXA »

Wenn du auf nummer gaaaanz sicher gehen willst, dann kannst du auch
die Funktion [c]RtlFillMemory_(Memory, Len, Byte)[/c] nutzen.
Bild
Toshy
Beiträge: 713
Registriert: 22.03.2005 00:29
Computerausstattung: Computer und Strom vorhanden
Wohnort: LK Wolfenbüttel

Beitrag von Toshy »

@MVXA
genau das sollte ja verhindert werden, weiß ich jedes bissl rechenpower sparen muß. wenn ich alles selbst machen muß, dann kann ich auch gleich wieder einen array nehmen wo alles gelöscht wird. aber das ist mir wieder zu lagsam, da es zu meinem proggi nicht paßt.
Wie mann das löscht usw. weiß ich, was ich nicht wirklich weiß ist, wie PB sich intern verhält beim schreiben einer Struktur.

@helpy
Ja wir reden aneinander vorbei.
Erstens ist es unerheblich was Allocatememory macht, denn ich nutze auch ReAllocatememory und da können daten drinn stehen die anders als NULL sind.
Und auch bei bei mit AllocateMemory erstellen Speicher können Daten drinn stehen die anders als NULL sind. Das nach allokieren des Speichers null drinn steht ist mir von anfang an bewußt, aber an der Stelle wo ich die Struktur nutze (mit Allocate erstellten Speicher) steht manchaml sehr wohl schon was drinn an Daten. Einfach weil ich es reinkopiert hatte. Zum Beispiel weil vorher schon mal mit einer Struktur reingeschrieben wurde, die Daten der Struktur aber schon in eine andere verschoben wurde.

Aus sicherheit könnte ich jetzt jedesmal alles mit Nullen überschreiben, was aber bei mir sehr viel, zu viel Rechenpower verbraucht. Daher würde helfen zu wissen wie PB intern damit umgeht.

Wir also der Stringpointer / Sting der Struktur freigegeben anhand einer internen Verwaltung der Struktur oder anhand des Pointers in der Struktur.
Je nachdem wie es gemacht wird hat es gravierende auswirkungen auf mein Programm sowie jedes andere.
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8809
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

@Friedrichs:
Also ich verstehe deine Probleme echt nicht. Worauf willst du denn
hinaus? Was musst du noch wissen? Hast du Beispielcode da, der so
kommentiert ist, dass man direkt weiß, wo du nicht sicher bist?
Deine Art, wie du das hier darstellst, verwirrt mich irgendwie, weil du
etwas durcheinander redest.

Vielleicht noch zwei, dreimal durchatmen und neu erklären für mich.
Danke! :wink:
Antworten