Lesen von großen Dateien in Java — Java heap space
Lese ich ein großer tsv-Datei (~40G) und dem Versuch, zu beschneiden, es durch das Lesen Zeile für Zeile und drucken Sie nur bestimmte Zeilen in eine neue Datei. Aber ich bekomme immer folgende exception:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2894)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:117)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:532)
at java.lang.StringBuffer.append(StringBuffer.java:323)
at java.io.BufferedReader.readLine(BufferedReader.java:362)
at java.io.BufferedReader.readLine(BufferedReader.java:379)
Unten ist der wichtigste Teil des Codes. Ich angegebenen Puffer-Größe 8192 nur für den Fall. Nicht Java deaktivieren Sie den Puffer, sobald die buffer size limit erreicht ist? Ich sehe nicht, was kann die Ursache für die große Arbeitsspeicher-Nutzung hier. Ich habe versucht, zur Erhöhung der heap-Größe, aber es machte keinen Unterschied (Maschine mit 4 GB RAM). Ich habe auch versucht, Spülen Sie die Ausgabe-Datei alle X Zeilen aber es hat auch nicht geholfen. Ich denke, vielleicht brauche ich, um Anrufe zu tätigen, um die GC, aber es klingt nicht richtig.
Irgendwelche Gedanken? Vielen Dank.
BTW - ich weiß, ich sollte rufen Sie trim() nur einmal auf, speichern es und verwenden es.
Set<String> set = new HashSet<String>();
set.add("A-B");
...
...
static public void main(String[] args) throws Exception
{
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile),"UTF-8"), 8192);
PrintStream output = new PrintStream(outputFile, "UTF-8");
String line = reader.readLine();
while(line!=null){
String[] fields = line.split("\t");
if( set.contains(fields[0].trim()+"-"+fields[1].trim()) )
output.println((fields[0].trim()+"-"+fields[1].trim()));
line = reader.readLine();
}
output.close();
}
- Funktioniert es drucken Sie alle Ergebnisse überhaupt? Muss es immer Blasen an der gleichen Stelle? Bist du sicher, dass es tatsächlich beim Lesen der Datei eine Zeile zu einem Zeitpunkt also identifizieren Sie die Zeile-beendet OK und es sind keine Leitungen lang genug, um es zu Haufen zu Blasen? Blöd zu Fragen, ich weiß...
- Was die -Xmx Einstellung verwenden Sie auf der JVM? Standardmäßig ist Java nicht der gesamte verfügbare RAM auf der Maschine, es sei denn, Sie geben Sie mit den-Xmx arg.
- mehr Speicher für Ihre Anwendung zu verstecken können einige wichtige design-Fehler, pop-up später. In 99.99% der Fälle, der Standard-Speicher ist genug und wenn nicht, dann machen Sie etwas falsch
- "In 99.99% der Fälle, der Standard-Speicher ist genug und wenn nicht, dann machen Sie etwas falsch", So, wie erklären Sie sich, dass die meisten Produktions-server und einigen Blöcken, viele java-app erhöhen Sie die Vorgaben ? 99,99% der Thesen sind dabei etwas falsch?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wahrscheinlich, was Los ist, dass die Datei nicht haben, Zeilenende-Zeichen und damit die Leser nur wächst es StringBuffer unbounded, bis es aus der Erinnerung.
Die Lösung wäre Lesen eine Feste Anzahl von bytes zu einer Zeit, mit der Verwendung von 'read ()' - Methode des Lesers, und suchen Sie dann für neue Linien (oder anderen Analyse-Token) innerhalb der kleineren Puffer(en).
Sind Sie sicher, dass die "Linien" in der Datei durch Zeilenumbrüche getrennte?
Habe ich 3 Theorien:
In der input-Datei ist nicht UTF-8, aber einige unbestimmte binäres format, das führt zu extrem langen Zeilen beim Lesen als UTF-8.
Die Datei enthält einige sehr lange "Linien" ... oder keine Zeilenumbrüche an alle.
Etwas anderes passiert im code, sind Sie nicht, uns zu zeigen; z.B. Sie sind das hinzufügen neuer Elemente zu
set
.Diagnostizieren diese:
od
(auf UNIX /LINUX), um zu bestätigen, dass die input-Datei enthält die gültigen Zeilenende-Zeichen; d.h. CR -, NB-oder CR-NL.Für den Datensatz, den etwas suboptimalen Einsatz von
trim
keinen Einfluss auf dieses Problem.Eine Möglichkeit ist, dass Sie running out of heap space während eine garbage collection. Die Hotspot-JVM verwendet eine parallel collector standardmäßig, was bedeutet, dass Ihre Anwendung können möglicherweise Objekte zuweisen schneller als der Sammler kann Sie zurückzugewinnen. Ich war in der Lage, die Ursache für ein Fehler wegen ungenügenden Speicherplatzes bei angeblich nur 10K Leben (kleinen) Objekten, die sich schnell reservieren und zu verwerfen.
Können Sie versuchen, statt mit dem alten (pre-1.5) serial collector mit der option
-XX:+UseSerialGC
. Es gibt einige andere "erweiterte" Optionen, die Sie verwenden können, um tune-Sammlung.Möchten Sie vielleicht zu versuchen, das entfernen des
String[] fields
Deklaration aus der Schleife. So erstellen Sie ein neues array, in jeder Schleife. Sie können einfach die Wiederverwendung der alten rechten?split()
). Seit seiner erforderlichen Umfang ist nur in der Schleife, es ist völlig in Ordnung, es zu erklären gibt.String[]
wird jedes mal erstellt, wennsplit()
genannt wird? Ich bekomme wenn @Shaunak kommt, wenn einString[]
erstellt wird (und der GC würde) auf jeden loop, würde es nicht effizienter sein, es zu erklären, bevor die Schleife, re-verwenden Sie es bei jeder iteration, dann setzen Sie ihn auf null (für GC) nach der Schleife? (Ich bin sicher, dies ist der Weg er gelehrt wurde, zurück in die J2ME-Tage!!!...)