Unten ist eine Klasse, die ich eine ConcurrentMap<String, List<String>>
in eine Datei schreibt. Der Schlüssel in der Map ist der Pfad, und der Wert in der Map muss sequenziell in die Datei geschrieben werden. Diese Task<Void>
wird jedes Mal gibt es 1.000 Werte in der Karte genannt:Java mehrere Öffnen und Schließen von Dateien zum Schreiben
public class MapWriter extends Task<Void> {
private final ParsingProducerConsumerContext context;
public MapWriter(ParsingProducerConsumerContext context) {
this.context = context;
}
@Override
protected Void call() throws Exception {
if (!isCancelled() || !context.isEmpty()) {
ConcurrentMap<String, List<String>> jsonObjectMap = context.fetchAndReset();
jsonObjectMap.entrySet().forEach((t) -> {
try {
FileUtils.writeLines(new File(context.getPath() + t.getKey() + "\\sorted.json"), t.getValue(), true);
} catch (IOException ex) {
context.getLogger().log("Error writing to disk:");
context.getLogger().log(ex.toString());
context.stopEverything();
}
});
context.getLogger().log(jsonObjectMap.values().stream().mapToInt(List::size).sum() + " schedules written to disk ");
} else {
context.getLogger().log("Nothing to write");
}
return null;
}
}
Während der ganzen Zeit diese Aufgabe ausgeführt wird, gibt es ein Produzent Task
eine ~ 2GByte Datei Zeile für Zeile zu lesen, die von einem Verbraucher verarbeitet wird und platziert in ConcurrentMap<String, List<String>>
.
Während dies funktioniert, ist es sehr langsam!
Meine Forschungsergebnisse legen nahe, dass beim wiederholten Öffnen und Schließen von Dateien ein erheblicher Overhead besteht, um die Leistung zu beeinträchtigen, und fragte sich, ob der folgende Ansatz besser sein könnte?
Pflegen Sie eine Map<String, File>
von File
Objekte, die offen sind. Wenn der Schlüssel in ConcurrentMap<String, List<String>>
einer geöffneten Datei entspricht, verwenden Sie diese File
Referenz zum Schreiben Wenn alle Verarbeitung abgeschlossen ist, durchlaufen Sie über Map<String, File>
Werte und schließen Sie jede Datei.
Klingt das sinnvoll? Es wären jedoch ca. 100 Dateien geöffnet.
EDIT :: Ich habe einen einfachen Benchmark mit System.nanoTime()
. Die Datei, die Zeile für Zeile vom Hersteller importiert wird, beträgt ca. 2 GB, und jede Zeile liegt zwischen 6 KB und 10 KB (in der List<String>
).
Außerdem ist ein OutOfMemory-Fehler aufgetreten! Ich nehme an, weil die 2GByte effektiv in den Speicher geladen werden und nicht schnell genug ausgeschrieben werden?
514 jsonObjects written to disk in 2258007ms 538 jsonObjects written to disk in 2525166ms 1372 jsonObjects written to disk in 169959ms 1690 jsonObjects written to disk in 720824ms 9079 jsonObjects written to disk in 5221168ms 22552 jsonObjects written to disk in 6943207ms 13392 jsonObjects written to disk in 6475639ms 0 jsonObjects written to disk in 6ms 0 jsonObjects written to disk in 5ms 0 jsonObjects written to disk in 5ms 40 jsonObjects written to disk in 23108ms 631 jsonObjects written to disk in 200269ms 3883 jsonObjects written to disk in 2054177ms Producer failed with java.lang.OutOfMemoryError: GC overhead limit exceeded
Für Vollständigkeit, hier ist der Produzent Klasse:
public class NRODJsonProducer extends Task<Void> {
private final ParsingProducerConsumerContext context;
public NRODJsonProducer(ParsingProducerConsumerContext context) {
this.context = context;
}
@Override
protected Void call() throws Exception {
context.getLogger().log("Producer created");
LineIterator li = FileUtils.lineIterator(new File(context.getPath() + context.getFilterFile()));
while (li.hasNext()) {
try {
context.getQueue().put(li.next());
} catch (InterruptedException ex) {
Logger.getLogger(NRODJsonProducer.class.getName()).log(Level.SEVERE, null, ex);
}
}
LineIterator.closeQuietly(li);
context.getLogger().log("Producer finished...");
return null;
}
}
Geben Sie es uns und lassen Sie es uns wissen? –