OutOfMemoryException: Wann werden Objekte freigegeben?

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Kiffi
Beiträge: 37
Registriert: Sa 27. Mär 2010, 11:39
OS, Lazarus, FPC: Windows 7 / Lazarus 1.0
CPU-Target: 32/64 bit

OutOfMemoryException: Wann werden Objekte freigegeben?

Beitrag von Kiffi »

Hallo,

mit unten stehendem Code lade ich ein ziemlich fettes CSV (knapp 500 MB) in eine TStringList*. Klappt auch.

Allerdings bekomme ich beim zweiten Aufruf eine OutOfMemoryException. Ich dachte eigentlich, dass ich
mit TSL.Free das Objekt wieder freigebe (und somit auch den Speicher). Im Taskmanager sieht man
zumindest, dass die RAM-Auslastung wieder auf den ursprünglichen Pegel (vor dem Laden) zurückfällt.

Muss ich sonst noch was mit der TSL machen? CLEARen? DESTROYen?

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  TSL: TStringList;
 
begin
 
  TSL := TStringList.Create();
  TSL.LoadFromFile('Dickes.Csv');
  TSL.Free;
 
  ShowMessage('Fertig');
 
end;
Danke im Voraus & Grüße ... Kiffi

* Solche Sachen mache ich normalerweise nicht; will mich nur ein wenig mit Lazarus / Free Pascal vertraut machen.

Live
Beiträge: 144
Registriert: So 22. Aug 2010, 16:06
OS, Lazarus, FPC: Backtrack 5 RC4 - 64bit Gnome
CPU-Target: 64bit
Wohnort: NRW
Kontaktdaten:

Re: OutOfMemoryException: Wann werden Objekte freigegeben?

Beitrag von Live »

Kiffi hat geschrieben:Hallo,

mit unten stehendem Code lade ich ein ziemlich fettes CSV (knapp 500 MB) in eine TStringList*. Klappt auch.

Allerdings bekomme ich beim zweiten Aufruf eine OutOfMemoryException. Ich dachte eigentlich, dass ich
mit TSL.Free das Objekt wieder freigebe (und somit auch den Speicher). Im Taskmanager sieht man
zumindest, dass die RAM-Auslastung wieder auf den ursprünglichen Pegel (vor dem Laden) zurückfällt.

Muss ich sonst noch was mit der TSL machen? CLEARen? DESTROYen?

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  TSL: TStringList;
 
begin
 
  TSL := TStringList.Create();
  TSL.LoadFromFile('Dickes.Csv');
  TSL.Free;
 
  ShowMessage('Fertig');
 
end;
Danke im Voraus & Grüße ... Kiffi

* Solche Sachen mache ich normalerweise nicht; will mich nur ein wenig mit Lazarus / Free Pascal vertraut machen.
Bau mal

Code: Alles auswählen

try
  new "TSL"
except
  On EMemoryError do
     write('Out of memory!');
end;
ein, damit prüfst du ob es wirklich am Speicher liegt. Normalerweise müsstest du ja genug Speicher zur Verfügung haben, oder etwa nicht?

Evtl kannst du noch versuchen die große CSV über mehrere Threads einzulesen, anstatt einem einzigen? Kenne das Problem eigtl nur von Windows-Servern *g*

Kiffi
Beiträge: 37
Registriert: Sa 27. Mär 2010, 11:39
OS, Lazarus, FPC: Windows 7 / Lazarus 1.0
CPU-Target: 32/64 bit

Re: OutOfMemoryException: Wann werden Objekte freigegeben?

Beitrag von Kiffi »

Hallo Live,
Live hat geschrieben:Normalerweise müsstest du ja genug Speicher zur Verfügung haben, oder etwa nicht?
ja, ist genügend Speicher da. Wenn nicht, müsste ja schon das erste Laden
schief laufen, oder?
Live hat geschrieben:Evtl kannst du noch versuchen die große CSV über mehrere Threads einzulesen, anstatt einem einzigen?
mir geht es ja nicht darum, die Datei unbedingt laden zu müssen / wollen. Der von
mir gepostete Code dient nur zur Veranschaulichung. Mich wundert nur, dass der
zweite Ladevorgang in die Hose geht, obwohl ich das Objekt freigegeben habe.

Danke & Grüße ... Kiffi

Benutzeravatar
theo
Beiträge: 10876
Registriert: Mo 11. Sep 2006, 19:01

Re: OutOfMemoryException: Wann werden Objekte freigegeben?

Beitrag von theo »

Bist du sicher, dass du nur diesen Code ausführst und nicht noch das ganze irgendwie zwischenspeicherst?

Wie viele Zeilen hat die Datei? Kommst du da womöglich an eine Grenze, wo das Verhalten Wischi-Waschi wird?
Die StringList speichert ja jede Zeile als String und TObject:
Das ist eine Zeile:
TStringItem = record
FString: string;
FObject: TObject;
end;

Deshalb sagt 500 MB allein vielleicht nicht so viel aus. Es kommt auch noch darauf an, auf wie vielen Zeilen die sind.
Im Extremfall, wenn die Datei nur aus 500 MB LF Zeichen besteht, hast du im Speicher
Das Zeichen+2*32bit für die Zeiger (auf 32bit Systemen) also
1Byte + 8Byte (Wahrsch. mehr weil ein AnsiString auch nicht nur aus 1 Byte besteht, müsste man nachschauen) pro Zeile.
Das knackt dann schnell mal die Grenze.

martin_frb
Beiträge: 586
Registriert: Mi 25. Mär 2009, 21:12
OS, Lazarus, FPC: Laz trunk / fpc latest release / Win and other
CPU-Target: mostly 32 bit

Re: OutOfMemoryException: Wann werden Objekte freigegeben?

Beitrag von martin_frb »

Das ist jetzt mal wilde Spekulation. Es könnte also auch was ganz anderes sein... Eine Möglichkeit:

Kompiliere mal mit einem anderem Mem-Manager: -gh

Beim ersten einlesen, ist dein Speicher noch ganz frisch (unfragmentiert), beim 2ten mal nicht.

So eine Stringliste beansprucht ja für jeden String ein eigenes Stück Speicher, wenn du also 10,000 Strings mit 70 Bytes hast, dann sind das 700kb
Aber wenn dein Speicher in 100 byte Stücken vorliegt, bleiben bei jedem String 30 Byte ungenutzt => 300 kb extra

Natürlich sollten beim Freigeben der ersten Liste in Theorie keine Fragmente entstehen (außer wenn während die Liste existierte, noch anderweitig Speicher belegt wurde. Z.b durch die LCL, beim behandeln des button-click)
=> so schlimm wie im Beispiel oben, wird es aber kaum werden...

Du musst auch mit einberechnen, das ein teil der liste (nicht der Strings, die liste sind die pointer auf die strings) zeitweilig doppelt existiert.
Diese liste wird mit einer bestimmten Größe vor-allocatiert. wenn dann mehr Einträge (Zeilen) gebraucht werden, muss neuer Speicher reserviert werden. Dazu wird zuerst neuer speicher (fuer die neue gesamt-Größe ) allocatiert, dann erst der alte freigegeben => kurz doppelt. (allerdings nur 8 16 auf 64 bit system) bytes pro Zeile).
Das bedeutet aber auch mehr fragmentation.....

Hast du mal
List.Capacity := 100000; // oder wie viele Zeilen du auch immer erwartest....
probiert?

Kiffi
Beiträge: 37
Registriert: Sa 27. Mär 2010, 11:39
OS, Lazarus, FPC: Windows 7 / Lazarus 1.0
CPU-Target: 32/64 bit

Re: OutOfMemoryException: Wann werden Objekte freigegeben?

Beitrag von Kiffi »

Hallo,

danke für die rege Teilnahme!

Die CSV besteht aus knapp 2.1 Mio Zeilen (Datensätze) mit je 20 Feldern.

Mir ist bewusst, dass das nicht grade eine kleine Datenmenge ist und normalerweise
würde ich die TStringList auch nicht verwenden, um eine so große Datei in einem
Rutsch in den Speicher zu laden. Meine bevorzugte Vorgehensweise wäre, die Datei
zeilenweise auszulesen und zu parsen.

Dennoch bleibt das grundsätzliche Problem: Wenn ich das TStringList-Objekt mit Free
freigebe, würde ich erwarten, dass der Speicher ordnungsgemäß aufgeräumt wird und
ich einen Zustand vorfinde, wie er vor dem Laden der Datei herrschte.

Demzufolge dürfte ich die Datei so oft erneut laden, wie ich will. Denn momentan müsste
ich ja immer damit rechnen, dass ein langlaufendes Programm irgendwann mal mit einem
OutOfMemory abstürzt, wenn Speicherbereiche verwendeter Objekte mit Free nicht ordentlich
aufgeräumt werden.

Wenn gewünscht, kann ich ja mal mein Test-Projekt (besteht wirklich nur aus einer Form, einem
Button und dem oben geposteten Code) nebst eigenerstellter CSV zur Verfügung stellen.

Danke & Grüße ... Kiffi

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6783
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: OutOfMemoryException: Wann werden Objekte freigegeben?

Beitrag von af0815 »

Kiffi hat geschrieben:Wenn gewünscht, kann ich ja mal mein Test-Projekt (besteht wirklich nur aus einer Form, einem
Button und dem oben geposteten Code) nebst eigenerstellter CSV zur Verfügung stellen.
Ich kanns dir in jedem Fall testen und meinen Senf dazugeben.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten