Laden...

Verhindern einer OutOfMemoryException, wenn die Datei als ganzes zu groß für den Hauptspeicher ist

Erstellt von theYoRecords vor 11 Jahren Letzter Beitrag vor 11 Jahren 9.905 Views
T
theYoRecords Themenstarter:in
73 Beiträge seit 2012
vor 11 Jahren
Verhindern einer OutOfMemoryException, wenn die Datei als ganzes zu groß für den Hauptspeicher ist

Hallo!

Zu diesem Thema konnte ich leider nicht viel und überhaupt nichts hilfreiches finden..
Wie verhindert man eine OutOfMemoryException? Klar, einfach nicht so viel Speicher verbrauchen...
Nehmen wir an es wird eine Datei gelesen, und der Inhalt bearbeitet. Ab einer gewissen Größe bekomme ich diese Exception, aber wüsste nicht was ich anders machen soll. Schließlich brauche ich die Datei im Arbeitsspeicher um mit ihr arbeiten zu können. Sie Stück für Stück zu bearbeiten wäre sehr umständlich und funktioniert in manchen Fällen sowieso nicht.
Kann mir da jemand weiterhelfen?

Danke im Voraus!

C
2.121 Beiträge seit 2010
vor 11 Jahren

Klar, einfach nicht so viel Speicher verbrauchen...

Gut erkannt. Was anderes geht nicht.

Bleibt nur noch die Überlegung wie man nicht so viel verbraucht.
Zum Beispiel eine Datei blockweise lesen und verarbeiten. Lieber umständlich als gar nicht. Falls das nicht geht, Pech gehabt. Dann muss mehr Speicher in die Kiste.
Falls das auch nicht geht, wirklich Pech gehabt.

Dann muss man von Anfang an drauf achten dass die Dateien nicht so groß werden.
Die Auslagerungsdatei (im Fall von Windows) fängt sowas ein bisschen ab. Aber mit der wird auch alles entsprechend langsam.

Hast du wirklich so riesige Dateien auf so schwachen Rechnern?

R
8 Beiträge seit 2007
vor 11 Jahren

Hi theYoRecords,

ich habe auch schon einige leidvolle Erfahrungen mit der OutOfMemoryException machen müssen. Nach meinen Erfahrungen wird diese in der Regel vom Framework geschworfen, wenn nicht mehr genügend freier Speicher am Stück auf dem Heap allokiert werden kann. Das hat nur bedingt etwas mit der Gesamtgröße des RAMs zu tun, eher mit der maximalen Größe des Speichers, der einem Prozess zugeteilt wird.
Bei 32bit-Prozessen sind das so um die 4 GB, die aber nicht komplett verwendet werden können.
Nun ist es bei der CLR so, dass Objekte > 85.000 Byte auf den so genannten "LOH (Large Object Heap)" geschoben werden. Dieser wird von der CLR niemals "kompaktiert" (so wie der SOH, Small Object Heap), so dass dort der Speicher irgendwann fragmentiert ist. Wenn dann ein weiteres sehr großes Objekt auf den Heap soll, kann es vorkommen, dass dort nicht mehr genug freier Speicher am Stück frei ist, obwohl insgesammt noch genug Platz im Prozessspeicher ist. Dann kommt die OutOfMemoryException.
Umgehen konnte ich das Problem bisher nur mit Auslagern der entsprechenden Aktivitäten in einen eigenen Prozess oder den Prozess als 64bit Prozess laufen zu lassen.
(Ich rede hier von der CLR 2 mit .NET 3.5, in der CLR des .NET 4 Frameworks wurden von MS ein paar Anpassungen mit dem LOH gemacht. Nicht ganz so aktuelle, aber immer noch gültige Infos gibt es in MSDN: CLR Inside Out: Large Object Heap Uncovered und auch z.B. in Codeproject: Trouble with the Large Object Heap. Etwas aktueller in MS Connect: Large Object Heap fragmentation causes OutOfmemoryException)

Hinweis von herbivore vor 11 Jahren

Auch wenn man im allgemeinen eine mögliche Fragmentierung bedenken muss, ist das laut der Beschreibung nicht das Problem des Threadstarters.

T
theYoRecords Themenstarter:in
73 Beiträge seit 2012
vor 11 Jahren

Vielen Dank für eure Antworten!

Vor Allem deine, realgun, hat mich ein Stück weitergebracht.

Aber schlussendlich werde ich nach jetzigem Wissensstand wohl nicht drum herum kommen, die Informationen Stück für Stück zu verarbeiten, wenn es für die zu verarbeitenden Dateien keine Größenbegrenzung geben soll..
Sehe ich das richtig?

Es wäre super wenn es es noch einen anderen Weg geben würde, da der Algorithmus schon jetzt recht lange für seine Arbeit braucht, und so noch um einiges länger brauchen wird. Aber vermutlich ist das wohl die einzige Möglichkeit.

C
2.121 Beiträge seit 2010
vor 11 Jahren

Kannst du deinen Algorithmus in ein paar Worten verständlich beschreiben?
Um welche Dateigröße geht es da etwa und was passiert mit den Daten?
Wie viel RAM ist in dem betroffenen Rechner?

Du siehst das schon richtig, wenn du erkennst dass du grundsätlich nicht mehr irgendwo rein kriegst als da drin Platz hat. Von daher kann man dir nur Tips geben wie du Speicherplatz sparen kannst (dass also keine Lücken entstehen) oder ähnliches, aber du kannst nunmal keine 3 GB in einen Rechner mit nur 2 GB reinladen. Auslagerungsdatei mal außen vor gelassen, wenn du die mit einkalkulierst kannst du gleich deine Datei nur stückchenweise lesen.

S
753 Beiträge seit 2006
vor 11 Jahren

Eine recht banale Antwort: Wenn du den Algorithmus oder das Verarbeiten nicht stückeln kannst, dann bleibt dir nicht viel mehr, als die Anwendung mit 64 bit zu kompilieren und soviel Arbeitsspeicher nach zulegen, bis die Datei als ganze gelesen werden kann.

Pagefile Größe könnte man Erhöhen, aber damit hast du keine Performance Vorteile gegenüber dem Stückeln. Auf Platte ist auf Platte

Life is a short

F
115 Beiträge seit 2012
vor 11 Jahren

Hallo,

folgende Links fand ich ganz erhellend:

.NET Memory usage - A restaurant analogy

“Out Of Memory” Does Not Refer to Physical Memory

Dort kann man verstehen, warum 64Bit und viel RAM das Problem nicht zwangsläufig beheben.

Gruß
f_igy

Hinweis von herbivore vor 11 Jahren

Auch wenn man im allgemeinen eine mögliche Fragmentierung und die Begrenztheit von anderen Speicherbereichen/-arten bedenken muss, ist das laut der Beschreibung nicht das Problem des Threadstarters.

R
8 Beiträge seit 2007
vor 11 Jahren

Mehr RAM erhöht "nur" die Performance (keine Auslagerung in Pagefile nötig), aber hat nichts mit dem maximalen Arbeitsspeicher eines Prozesses zu tun.
Auch auf einem PC mit 512 MB RAM wird einem 32bit Prozess ein 4GB großer Addressraum für den Arbeitsspeicher zugeteilt (welcher aber nicht vollständig nutzbar ist).
Einzig das Betriebssystem entscheidet welcher Teil davon im RAM und welcher auf der Festplatte liegt.
Der Umstieg auf 64bit könnte hier möglicherweise schon helfen. Ob es bei wenig RAM durch die Auslagerungen in das Pagefile auch performant ist, steht natürlich auf einem anderen Blatt.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo theYoRecords,

dass man intelligente (oder zumindest angepasste) Algorithmen bzw. Zugriffsstrategien braucht, wenn die zu verarbeitenden Dateien oder Daten nicht gleichzeitig vollständig in den Hauptspeicher passen und auch nicht einfach sequentielle verarbeitet werden können, zeigt sich exemplarisch bei dem Problem des External sorting. Möglicherweise kannst du einige der dortigen Hinweise auf dein Problem übertragen.

herbivore

W
872 Beiträge seit 2005
vor 11 Jahren

Seit .NET 4.5 gibt es gcAllowVeryLargeObjects als Setting - damit wird einiges moeglich, was vorher nicht ging....

T
theYoRecords Themenstarter:in
73 Beiträge seit 2012
vor 11 Jahren

Vielen Dank für eure Antworten!

Das dürfte eine solide Grundlage für die Lösung des Problems sein. Sobald ich Zeit habe werde ich mir das alles mal genau anschauen und durch den Kopf gehen lassen.

@chilic:
Es geht hierbei um einen Verschlüsselungs-Algorithmus der String- und Dateiverschlüsselung unterstützt, aber das spielt eigentlich keine Rolle. Das gleiche Problem tritt bei jedem Algorithmus bzw. Programm auf, das unbeschränkte Dateigrößen unterstützen soll (jedes Programm, das mit Dateien arbeitet, sei es jetzt WinZip, Word oder einfach nur das Standard-Notepad).

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo theYoRecords,

... bei jedem Algorithmus bzw. Programm auf, das unbeschränkte Dateigrößen unterstützen soll

es macht aber schon einen Unterschied, ob die Daten sequentiell verarbeitet werden können oder wahlfreier Zugriff auf den gesamten Datenbestand nötig ist.

Bei sequentieller Verarbeitung, wie sie beim Zippen oder Verschlüsseln üblich ist, ...* sollte sich sich der Code relativ leicht auf byte-, zeilen- oder blockweise Verarbeitung umstellen lassen,

  • sollte sich die Laufzeit durch diese Umstellung nicht ändern (oder sogar beschleunigen),
  • ist die byte-, zeilen- oder blockweise Verarbeitung vorzuziehen, wenn zu erwarten ist, dass Dateien verarbeitet werden, die fast den gesamten Hauptspeichers oder sogar mehr belegen würden.

herbivore

T
theYoRecords Themenstarter:in
73 Beiträge seit 2012
vor 11 Jahren

Vielen Dank für deine Antwort!

Dass es dadurch schneller wird kann ich mir kaum vorstellen. Außerdem wird das Unterfangen bzw. zumindest die Entschlüsselung dadurch um einiges komplizierter und es entstehen, glaube ich zumindest, zumindest bei meinem Verfahren noch ein paar weitere Nachteile. Aber das werde ich alles demnächst sehen. Eine andere Möglichkeit habe ich sowieso nicht.

84 Beiträge seit 2007
vor 11 Jahren

Vielleicht hilft es, wenn du deinen eigentlichen Algorithmus zunächst unangetastet lässt.
Stattdessen könntest du eine Klasse implementieren, welche die Zugriffe auf das Dateisystem verwaltet.

In deinem Algorithmus-Code verwendest du dann z.B. statt dem StreamWriter deine eigene Klasse, der Einfachheit halber kann die Schreiben-Methode dort ja ähnlich aufgebaut sein, wie beim StreamWriter.

So könntest du in Ruhe an der Umsetzung der von deinem Algorithmus angeforderten Lese-/Schreibzugriffe arbeiten.
Dein Algorithmus verwendet das Ganze dann als Black-Box - der Unterschied bestünde lediglich darin, dass Zugriffe dann durch die dahinterstehende Zugriffsstrategie mehr Zeit benötigen.

Ich könnte mir vorstellen, dass es der Übersichtlichkeit nicht gut tut, wenn man die beiden Aufgaben vermengt.

Viele Grüße

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Razer,

grundsätzlich können Abstraktionsschichten sehr nützlich sein. Probleme tauchen aber immer dann auf, wenn sich die Aufwandsklasse der Zugriffe oder die Größenordnung der durchschnittlichen Zugriffszeit ändert.

Der springende Punkt bleibt der, den ich schon angesprochen habe. Wenn Zugriffe sequentiell erfolgen, also ein Wert nach dem anderen gelesen und verarbeitet wird und dabei jeder Wert nur einmal (vom Hintergrundspeicher) gelesen wird, dann ist eine Umstellung von "alles auf einen Rutsch in den Hauptspeicher lesen" zu einer byte-, zeilen- oder blockweise Verarbeitung einfach und beeinflusst auch nicht das Laufzeitverhalten.

Aber theYoRecords behaupte ja, dass eine Änderung bei ihm kompliziert wäre. Das wäre sie aber nur, wenn die die Daten eben doch nicht sequentiell verarbeitet werden, sondern wenn immer wieder auf (weit) zurückliegende oder (weit) vorausliegende Daten zugegriffen werden müsste. Dann aber müsste eine solche Abstraktionsschicht solche Zugriffe auf der Platte ausführen, denn wir gehen ja gerade davon aus, dass die Daten in ihrer Gesamtheit nicht in dem Speicher passen. Plattenzugriffe sind aber um Größenordungen langsamer als Hauptspeicher- (oder gar Prozessorcache-)Zugriffe.

Und damit ist die Voraussetzung aus dem ersten Absatz gegeben, dass eine Abstraktionsschicht Probleme macht. Übertragen auf das Sortierbeispiel wäre das so, als wenn man große Datenmengen auf einem (langsamen) externen Medium per Abstraktionsschicht mit einem Algorithmus sortieren würde, der für die Verarbeitung im Hauptspeicher gedacht ist, statt einem Algorithmus, der für das Sortieren auf einem (langsamen) externen Medium gedacht ist. Das würde zwar theoretisch (irgendwann) die funktional richtigen Ergebnisse liefern, aber die Laufzeiten wären fatal.

herbivore

5.657 Beiträge seit 2006
vor 11 Jahren

Aber letztendlich kann man darüber nur spekulieren, solange wir nicht wissen, um welchen Algorithmus es sich dabei handelt...
Christian

Weeks of programming can save you hours of planning

1.346 Beiträge seit 2008
vor 11 Jahren

Ich glaube an dieser Stelle kann man mal die Memory Mapped Files erwähnen. Damit kannst du dann die Entscheidung, was im Ram, und was nur im Hauptspeicher liegt dem Betriebssystem überlassen.

LG pdelvo

T
theYoRecords Themenstarter:in
73 Beiträge seit 2012
vor 11 Jahren

Das sind alles sehr interessante Ansätze, vielen Dank!

Mit so viel Input werde ich sicher eine gute Lösung finden. Im Moment ist aber ein anderes Projekt dazwischen gekommen, also kann ich mich im Moment leider nicht ganz diesem Problem widmen.