Wie vermeide ich mapFailed () Fehler beim Schreiben in große Datei auf System mit begrenztem Speicher

8

Ich habe gerade einen Fehler in meinem opensrc-Bibliothekscode festgestellt, der einen großen Puffer für das Vornehmen von Änderungen an einer großen flac-Datei zur Verfügung stellt. Der Fehler tritt nur auf einer alten PC-Maschine mit 3Gb-Speicher unter Verwendung von Java 1.8.0_74 25.74-b02 32bit auf

Ursprünglich habe ich nur einen Puffer zugewiesen

%Vor%

Aber seit einiger Zeit habe ich es als

%Vor%

Mein (falsches) Verständnis bestand darin, dass gemappte Puffer weniger Speicher belegen als ein direkter Puffer, weil der gesamte gemappte Puffer nicht nur zur gleichen Zeit im Speicher sein muss, sondern nur der Teil, der verwendet wird. Aber diese Antwort sagt, dass die Verwendung von abgebildeten Byte-Puffern eine schlechte Idee ist, so dass ich nicht klar, wie es funktioniert

Java Large File Upload wirft java.io. IOException: Karte fehlgeschlagen

Der vollständige Code ist unter hier

    
Paul Taylor 11.11.2016, 10:11
quelle

2 Antworten

2

Obwohl ein gemappter Puffer möglicherweise zu einem bestimmten Zeitpunkt weniger physischen Speicher benötigt, benötigt er immer noch einen verfügbaren (logischen) Adressraum, der der gesamten (logischen) Größe des Puffers entspricht. Um die Dinge noch schlimmer zu machen, erfordert es (wahrscheinlich), dass der Adressraum zusammenhängend ist. Aus irgendeinem Grund scheint dieser alte Computer nicht in der Lage zu sein, ausreichend zusätzlichen logischen Adressraum bereitzustellen. Zwei wahrscheinliche Erklärungen sind (1) ein begrenzter logischer Adressraum + hohe Pufferspeicheranforderungen und (2) einige interne Einschränkungen, die das OS der Speichermenge auferlegt, die als Datei für E / A abgebildet werden kann.

In Bezug auf die erste Möglichkeit, bedenken Sie, dass in einem virtuellen Speichersystem jeder Prozess in seinem eigenen logischen Adressraum ausgeführt wird (und somit Zugriff auf die volle Adressierung von 2 ^ 32 Bytes hat). Wenn also - zu dem Zeitpunkt, an dem Sie versuchen, MappedByteBuffer zu instanziieren - die aktuelle Größe des JVM-Prozesses plus die gesamte (logische) Größe von MappedByteBuffer größer ist als 2 ^ 32 Byte (~ 4 Gigabyte), dann würden Sie auf OutOfMemoryError stoßen (oder auf einen Fehler / eine Ausnahme, die diese Klasse auswählt, zB IOException: Map failed ).

Was die zweite Möglichkeit betrifft, ist wahrscheinlich der einfachste Weg, dies zu bewerten, Ihr Profil / die JVM zu profilieren, während Sie versuchen, MappedByteBuffer zu instanziieren. Wenn der reservierte Speicher des JVM-Prozesses + die erforderliche totalTargetSize deutlich unter der Obergrenze von 2 ^ 32 Byte liegt, aber Sie immer noch einen Fehler "map failed" erhalten, ist es wahrscheinlich, dass ein internes Betriebssystem die Speichergröße begrenzt Dateien sind die Ursache.

Was bedeutet das also so weit wie möglich Lösungen?

  1. Benutze den alten PC einfach nicht. (vorzuziehen, aber wahrscheinlich nicht machbar)
  2. Stellen Sie sicher, dass alles andere in Ihrer JVM für die Lebensdauer von MappedByteBuffer einen möglichst geringen Speicherbedarf hat. (plausibel, aber vielleicht irrelevant und definitiv unpraktisch)
  3. Brechen Sie diese Datei in kleinere Teile auf und arbeiten Sie dann jeweils nur an einem Stück. (hängt möglicherweise von der Art der Datei ab)
  4. Verwenden Sie einen anderen / kleineren Puffer. ... und ertragen nur die verringerte Leistung. (das ist die realistischste Lösung, auch wenn es am frustrierendsten ist)

Was genau ist totalTargetSize für Ihren Problemfall?

BEARBEITEN:

einige graben Nach tun, so scheint es klar, dass die IOException ist darauf zurückzuführen, in einem aus Adressraum läuft 32-Bit-Umgebung . Dies kann selbst dann passieren, wenn die Datei selbst unter 2 ^ 32 Bytes liegt, entweder aufgrund des Fehlens eines ausreichenden zusammenhängenden Adressraums oder aufgrund anderer ausreichend großer Adressraumanforderungen in der JVM zur gleichen Zeit mit der großen MappedByteBuffer Anfrage kombiniert ( Kommentare sehen ). Um klar zu sein, kann ein IOE noch eher geworfen wird als ein OOM auch wenn die ursprüngliche Ursache ist ENOMEM . Darüber hinaus scheint es Probleme mit älteren [32-Bit-Umgebungen hier einfügen] zu geben ( Beispiel , Beispiel ).

Es sieht also so aus, als hätten Sie drei Hauptmöglichkeiten.

  1. Verwenden Sie " die 64-Bit-JRE oder ein anderes Betriebssystem ".
  2. Verwenden Sie einen kleineren Puffer eines anderen Typs und bearbeiten Sie die Datei in Blöcken. (und nehmen Sie den Leistungstreffer, weil kein gemappter Puffer verwendet wird)
  3. Verwenden Sie weiterhin MappedFileBuffer aus Gründen der Leistung, aber arbeiten Sie auch mit der Datei in kleineren Blöcken, um die Adressraumbeschränkungen zu umgehen.

Der Grund, warum ich MappedFileBuffer in kleineren Chunks als drittes verwende, liegt an den gut etablierten und ungelösten Problemen beim Unmapping von MappedFileBuffer (Beispiel ), was Sie zwischen den einzelnen Chunks machen müssten, um zu vermeiden, dass die 32-Bit-Obergrenze aufgrund des kombinierten Adressraum-Footprints der akkumulierten Mappings überschritten wird . (HINWEIS: Dies gilt nur, wenn es sich um die 32-Bit-Adressraumobergrenze und nicht um einige interne Betriebssystemeinschränkungen handelt. Wenn letzteres der Fall ist, dann ignorieren Sie diesen Absatz) Sie könnten diese Strategie (lösche alle Referenzen und führe dann den GC aus), aber im Prinzip bist du der Gnade ausgeliefert Der GC und das zugrunde liegende Betriebssystem interagieren mit Speicherabbilddateien. Und andere potenzielle Problemumgehungen, die versuchen, die zugrunde liegende speicherdefinierte Datei mehr oder weniger direkt zu manipulieren ( Beispiel ) sind äußerst gefährlich und speziell von Oracle verurteilt ( siehe letzten Absatz ). Schließlich, wenn man bedenkt, dass GC-Verhalten sowieso unzuverlässig ist, Darüber hinaus heißt es in der offiziellen Dokumentation ausdrücklich, dass " viele der Details von Speicherkarten [sind] nicht spezifiziert ", ich würde nicht empfehlen, MappedFileBuffer trotzdem zu verwenden von jeder Problemumgehung, über die Sie lesen können.

Wenn Sie also nicht bereit sind, das Risiko einzugehen, würde ich vorschlagen, entweder Oracles ausdrücklichen Rat zu befolgen (Punkt 1) oder die Datei als Folge kleinerer Chunks mit einem anderen Puffertyp (Punkt 2) zu verarbeiten. p>     

Travis 06.12.2016, 05:35
quelle
1

Wenn Sie Puffer zuweisen, erhalten Sie praktisch einen Teil des virtuellen Speichers von Ihrem Betriebssystem (und dieser virtuelle Speicher ist endlich und oberer theoretischer Wert ist Ihr RAM + welcher Swap auch konfiguriert ist - was auch immer zuerst von anderen Programmen und Betriebssystemen übernommen wurde) / p>

Die Speicherzuordnung fügt Ihrem virtuellen Speicher nur den belegten Speicherplatz auf Ihrer Festplatte hinzu (ok, es gibt einen gewissen Overhead, aber nicht so viel) - damit Sie mehr davon bekommen können.

Keine davon muss ständig im RAM vorhanden sein, Teile davon können jederzeit auf die Festplatte ausgelagert werden.

    
Konstantin Pribluda 05.12.2016 17:33
quelle

Tags und Links