Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Memory Leak bei TStringList.Create
mczero098 - Mo 16.01.12 15:09
Titel: Memory Leak bei TStringList.Create
Halli Hallo,
anbei, ich bin nicht ganz neu hier im Forum, bzw. war zuvor immer nur lesend tätig.. Aber nun wirds mal Zeit das
ich auch selber das Forum nutze.. ich komme nämlich nicht weiter!!
Ich habe hier eine Methode, die eine StringList zurückgibt. Der Methode wird ein String übergeben, dieser wird dann quasi ähnlich der PHP-Funktion explode() als StringList zurückgegeben, das ist im Prinzip alles, was die Methode macht. Sie soll aus Strings wie "23/24" oder "20-3/26" praktisch die einzelnen, maximal zweistelligen Zahlen extrahieren.
So weit, so schön! Alles funktioniert. Jedoch beim ca. 136ten durchlauf hängt die Anwendung in der ersten Zeile TStringList.Create und der Speicherverbrauch steigt, bis eine EOutOfMemory Exception kommt.
Ich habe FastMM ausprobiert, jedoch wird das LogFile selber, nachdem die Exception auftritt, Gigabyteweise groß.. Bekomme sie so nicht geöffnet..
Kann mir einer erklären, was da vor sich geht?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33:
| function TpdoxImport.splitRSatz(kombinationsSatz: String): TStringList; var i : integer; resultStringList : TStringList; singleRSatz : String; begin resultStringList := TStringList.Create; i := 1; while i <= length(kombinationsSatz) do begin singleRSatz := ''; if regExprZahlen.Exec(kombinationsSatz[i]) then begin singleRSatz := singleRSatz + kombinationsSatz[i]; if i < length(kombinationsSatz) then if regExprZahlen.Exec(kombinationsSatz[i+1]) then begin singleRSatz := singleRSatz + kombinationsSatz[i+1]; i := i + 2; end else begin i := i + 1; end; resultStringList.Add(singleRSatz); end else begin i := i + 1; end; end; result := resultStringList; end; |
Ach ja, ich arbeite mit Delphi 7 Enterprise.
Vielen Dank im Voraus für die Mühen!! Ich bin gerade ratlos/oder stehe einfach nur auf dem Schlauch.
jaenicke - Mo 16.01.12 15:24
Wo wird die Stringliste denn wieder freigegeben? Grundsätzlich gibt es eigentlich nur drei sinnvolle Möglichkeiten:
- Erstellen im Konstruktor, Freigabe im Destruktor
- Erstellen vor einem Methodenaufruf, freigeben danach
- Direkt erstellen und in der selben Methode wieder freigeben
Dabei gibt man die Ressource immer an der selben Stelle / Ebene wieder frei, an der man sie reserviert hat. Deine Methode (ein erstelltes Objekt zurückliefern :shock:) ist dagegen äußerst unsauber...
Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| procedure TpdoxImport.splitRSatz(kombinationsSatz: String; AValues: TStringList);
... var MyList: TStringList; begin MyList := TStringList.Create; try splitRSatz('', MyList); ... finally MyList.Free; end; end; |
Tranx - Mo 16.01.12 15:28
Genau das wollte ich auch schreiben, aber Sebastian kam mir zuvor. Was mit create erzeugt wurde, muss letztlich auch u.a. mit free wieder freigegeben werden, wie Sebastian schrieb. Außerdem denke ich nicht, dass Du diese Split-Ergebnisse alle speicherst, sondern nur jeweils bei der Prozedur benötigst, später dann gefahrlos löschen kannst (siehe Sebastians Konstrukt).
mczero098 - Mo 16.01.12 15:36
Ja das stimmt, das Objekt wird außerhalb der Methode wieder freigegeben. Ich dachte aber immer, das wäre OK,
ich bin ja noch nicht ewig Delphi Entwickler und zuvor wurde mir die Praxis in JAVA genau so desöfteren beigebracht *schäm*
Denn gedanklich ging ich immer davon aus, das hier immer nur mit einem Objekt gehandelt wird, jedoch mit wechselnden Referenzen darauf..
Ich probiere mal die Erstellung außerhalb der Methode.. Danke schon einmal..
Tranx - Mo 16.01.12 15:50
Das Entscheidende ist immer, dass es auf der gleichen Ebene freigegeben wird, auf der es erzeugt wird. Denn Du weißt ja nicht, in wieweit z.B. die Prozedur nicht doch merhfach aufgerufen wird, bevor die Freigabe erfolgt. Und dann ist nur eine Komponente freigegeben, die andere/n nicht. Schau dir Sebastians Kosntrukt an. Dort ist es auf der selben Ebene und nur ein Aufruf pro Erzeugung und Freigabe möglich. Selbst wenn ein Fehler auftritt, wird die Freigabe abgearbeitet. Ansonsten würde die Prozedur (ohne den Try .. finally .. end Konstrukt) möglicherweise ohne Freigabe abgearbeitet.
jaenicke - Mo 16.01.12 15:55
mczero098 hat folgendes geschrieben : |
ich bin ja noch nicht ewig Delphi Entwickler und zuvor wurde mir die Praxis in JAVA genau so desöfteren beigebracht *schäm* |
Dort ist das auch in Ordnung, denn Java hat (wie .NET) einen Garbage Collector und gibt die Objekte selber wieder frei. ;-)
zuma - Mo 16.01.12 16:21
Stringlisten sind toll, wenn man sie geschickt benutzt und können einem ne Menge Arbeit abnehmen ;)
ich erlaub mir mal so eine kleine 'Klugscheißerei',
was mit Stringlisten alles so geht (anhand der von dir gegebenen Daten):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| procedure StringZerlegen(xString : String; xTrenner : char; xListe : TStringList); var lSl : TStringList; begin lSl := TStringList.Create; try lSl.Delimiter := xTrenner; lSl.StrictDelimiter := True; lSl.DelimitedText := xString; xListe.Text := lSl.Text; finally lSl.Free; end; end;
procedure THauptmenu.Button2Click(Sender: TObject); var lSl : TStringList; x : integer; begin inherited; lSl := TStringList.Create; try StringZerlegen('23/24;20-3/26', ';', lSl); ShowMessage(lSl.Text); StringZerlegen(lSl.Text, '/', lSl); ShowMessage(lSl.Text); StringZerlegen(lSl.Text, '-', lsl); ShowMessage(lSl.Text); finally lSl.Free; end; end; |
mczero098 - Mo 16.01.12 16:31
Es läuft nun! Allerdings war das Problem die While-Schleife in der Methode..
Wenn der Eingabestring nur einstellig ist (und das war er beim x-ten Durchlauf...), dann ist das natürlich eine
Endlosschleife.. ich war blind :D Und heute vollkommen übermüdet.. Warum er vorhin allerding bei xxx := TStringList.Create hing weiß ich nicht..
muss wohl wie gesagt mit der unsauberen Erzeugung/Zerstörung zusammenhängen.
@Tranx Abgesehen vom eigentlich Problem habe ich nun wie schon vorgeschlagen Erzeugung und Zerstörung an einem Ort erledigt :)
@jaenicke jup da hast Du recht.. da ist ein wenig Umdenken meinerseits erforderlich! Das Prinzip habe ich eigentlich verstanden, nur war ich ein wenig sorglos an welchen Stellen ich was erzeuge und wieder freigebe..
@zuma Sieht nett aus! Das man da mit Delimitern arbeiten kann war mir gar nicht bewusst :D
Generell verspüre ich oft den Wunsch, einfach öfter zu Wissen was als Sauber und was als Unsauber gilt. Auch wenn mein obiges Negativbeispiel ja eigentlich einleuchten sollte!
Gibts irgendwo Tutorials ála "Delphi Clean Code" ?
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!