2010-06-01 11 views
9

Ich habe eine Parser-Klasse für ein bestimmtes Binärformat (nfdump, falls jemand interessiert ist) geschrieben verwendet java.nios MappedByteBuffer, um Dateien von jeweils einigen GB zu lesen. Das binäre Format besteht nur aus einer Reihe von Headern und meist binären Sätzen mit fester Größe, die durch Aufruf von nextRecord() an den Aufrufer ausgegeben werden, der auf den Zustandsautomaten drückt und nach Beendigung NULL zurückgibt. Es funktioniert gut. Es funktioniert auf einer Entwicklungsmaschine.Java map/nio/NFS-Problem, das einen VM-Fehler verursacht: "Ein Fehler ist in einem kürzlich unsicheren Speicherzugriffsvorgang in kompiliertem Java-Code aufgetreten"

Auf meinem Produktions-Host kann es für ein paar Minuten oder Stunden laufen, aber immer scheint zu werfen "java.lang.InternalError: ein Fehler trat in einem kürzlich unsicheren Speicherzugriffsvorgang in kompiliertem Java-Code" Fingersatz einer der die Methoden Map.getInt, getShort, dh eine Leseoperation in der Map. (?)

Der unumstritten Code, der die Karte einrichtet, ist dies:

/** Set up the map from the given filename and position */ 
    protected void open() throws IOException { 
      // Set up buffer, is this all the flexibility we'll need? 
      channel = new FileInputStream(file).getChannel();  
      MappedByteBuffer map1 = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); 
      map1.load(); // we want the whole thing, plus seems to reduce frequency of crashes? 
      map = map1; 
      // assumes the host writing the files is little-endian (x86), ought to be configurable 
      map.order(java.nio.ByteOrder.LITTLE_ENDIAN); 
      map.position(position); 
    } 

und dann benutze ich die verschiedenen map.get * Methoden Shorts, Ints, sehnt sich und andere Sequenzen von Bytes zu lesen, bevor Treffen Sie das Ende der Datei und schließen Sie die Karte.

Ich habe noch nie die Ausnahme auf meinem Entwicklungs-Host geworfen gesehen. Aber der wesentliche Unterschied zwischen meinem Produktions-Host und der Entwicklung besteht darin, dass ich auf der ersten Seite Sequenzen dieser Dateien über NFS lese (wahrscheinlich 6-8 TB, die schließlich noch wachsen). Auf meinem Dev-Rechner habe ich eine kleinere Auswahl dieser Dateien lokal (60 GB), aber wenn es auf dem Produktions-Host explodiert, ist es normalerweise gut, bevor es 60 GB an Daten erreicht.

Beide Maschinen laufen mit Java 1.6.0_20-b02, obwohl auf dem Produktionshost Debian/Lenny läuft, der Dev-Host ist Ubuntu/Karmic. Ich bin nicht überzeugt, dass das einen Unterschied machen wird. Beide Computer verfügen über 16 GB RAM und werden mit den gleichen Java-Heap-Einstellungen ausgeführt.

Ich bin der Ansicht, dass, wenn es einen Fehler in meinem Code gibt, genug von einem Fehler in der JVM, um mich nicht eine richtige Ausnahme zu werfen! Aber ich denke, es ist nur ein bestimmter JVM-Implementierungsfehler aufgrund von Interaktionen zwischen NFS und mmap, möglicherweise eine Wiederholung von 6244515, die offiziell behoben ist.

Ich habe bereits versucht, einen "load" Aufruf hinzuzufügen, um den MappedByteBuffer zu zwingen, seinen Inhalt in den RAM zu laden - dies schien den Fehler in dem einen Testlauf zu verzögern, den ich gemacht habe, aber nicht verhindern. Oder es könnte Zufall sein, dass es das längste war, bevor es zusammenbrach!

Wenn Sie so weit gelesen haben und mit java.nio schon einmal so etwas gemacht haben, was wäre Ihr Instinkt? Gerade jetzt meins ist, um es ohne nio umzuschreiben :)

+0

Ich vermute, Sie haben bereits D8 von gesehen (http://nfs.sourceforge.net/) – Justin

+0

Ich hatte nicht, danke, aber dann schreibe ich auch nicht in diese Dateien. –

+0

Ich sehe dies bei Speicherabbilddateien auf lokalen ext4 und tmpfs Dateisystemen mit Java 7u1 auftreten. –

Antwort

4

Ich würde es ohne Verwendung von gemappten NIO umschreiben. Wenn Sie mit mehr als einer Datei arbeiten, gibt es ein Problem, dass der zugeordnete Speicher niemals freigegeben wird, so dass Ihnen der virtuelle Speicher ausgehen wird: NB dies ist nicht unbedingt nur ein OutOfMemoryError, der mit dem Garbage Collector interagiert Fehler beim Zuweisen des neuen zugeordneten Puffers. Ich würde einen FileChannel verwenden.

Allerdings sind groß angelegte Operationen auf NFS-Dateien immer extrem problematisch. Es wäre viel besser, wenn Sie das System so umgestalten, dass jede Datei von der lokalen CPU gelesen wird. Sie werden auf diese Weise auch immense Geschwindigkeitsverbesserungen erhalten, weit mehr als die 20%, die Sie verlieren werden, wenn Sie keine zugeordneten Puffer verwenden.

+0

Ich dachte an fehlenden virtuellen Adressraum, aber wie Sie sagten, sollte sich das in einem Mapping-Fehler manifestieren (plus ich lese nur eine Datei gleichzeitig und auf einem 64-Bit-System). Ich werde wahrscheinlich die Server neu anordnen, so dass die Dateien auf dem gleichen Server wie der Java-Prozess leben, und vermeiden, was auch immer NFS-Problem ist. Kurz gesagt, ich lese alles nur in einen ByteBuffer, aber weil mehrere Threads die gleichen Dateien lesen, oft zur selben Zeit, bringt es Sachen wieder, die mmap * sollte * eine elegante Lösung sein! –

+0

Ja, ich hatte auf eine Antwort gehofft, die mich mmap halten ließ, ich brauchte nur einen Push für jemand anderen, der sagte "es wird nicht funktionieren" :) Der open() Code liest jetzt gerade das ganze Los in ein zugewiesener ByteBuffer. Während mein Instinkt war, sich Gedanken über Speichervergeudung zu machen (wie mehrere Leser = mehrere Kopien auf dem Haufen), habe ich keinen Leistungsabfall im Vergleich zu früheren Läufen gesehen, also kann ich mich nicht wirklich beschweren. Ich habe den alten Code kommentiert in der Hoffnung, dass ich die "elegante" mmap wiederherstellen kann, aber unter der Annahme, dass meine Nfdump-Dateien die gleiche Größe bleiben, werde ich sie wahrscheinlich nicht wieder brauchen. –

+0

'mehrere Leser = mehrere Kopien auf dem Haufen': nur wenn Sie diese mehrere Kopien machen. Kannst du nicht eine Art Singleton-Zugang organisieren? – EJP