In meinem Projekt müssen wir eine sehr große Datei lesen, in der jede Zeile durch ein Sonderzeichen ("|") getrennt gekennzeichnet ist. Leider kann ich keine Parallelität verwenden, da es notwendig ist, zwischen dem letzten Zeichen einer Zeile und dem ersten Zeichen der nächsten Zeile eine Validierung durchzuführen, um zu entscheiden, ob sie extrahiert wird oder nicht. Wie auch immer, die Anforderung ist sehr einfach: Teile die Linie in Token, analysiere sie und speichere nur einige von ihnen im Speicher. Der Code ist sehr einfach, etwas wie unten:String.split() temporäre Objekte und Garbage Collect
Aber dieses kleine Stück Code ist sehr, sehr ineffizient. Die Methode split() erzeugt zu viele temporäre Objekte, die nicht gesammelt werden (wie am besten hier erklärt. http://chrononsystems.com/blog/hidden-evils-of-javas-stringsplit-and-stringr
Zum Vergleich: eine 5 MB-Datei wurde um 35 MB Speicher am Ende der Datei Prozess mit
.getestet habe ich einige Alternativen wie:
- eine Pre kompilierten Muster verwenden (Performance of StringTokenizer class vs. split method in Java)
- Verwenden Guava der Splitter (Java split String performances)
- optimieren String storag e (http://java-performance.info/string-packing-converting-characters-to-bytes/)
- Einsatz optimierter Sammlungen (http://blog.takipi.com/5-coding-hacks-to-reduce-gc-overhead)
Aber keiner von ihnen scheint effizient genug zu sein. Mit Hilfe von JProfiler konnte ich feststellen, dass die Speicherkapazität temporärer Objekte zu hoch ist (35 mb verwendet, aber nur 15 mb werden tatsächlich von gültigen Objekten verwendet).
Dann entscheide ich mich für einen einfachen Test: nach 50.000 Zeilen lesen, explizite Aufruf von System.gc(). Am Ende des Prozesses ist die Speicherbelegung von 35 MB auf 16 MB gesunken. Ich habe viele, viele Male getestet und immer das gleiche Ergebnis bekommen.
Ich weiß aufrufen, dass der Aufruf von System.gc() ist eine schlechte Praxis (wie in Why is it bad practice to call System.gc()? angezeigt). Aber gibt es in einem Cenario eine andere Alternative, bei der die Methode split() millionenfach aufgerufen werden kann?
[UPDATE] Ich benutze einen 5 mb nur für Testzwecke Datei, aber das System soll, ist hier viel größere Dateien (500 MB ~ 1 Gb)
* "Die Methode split() generiert zu viele temporäre Objekte, die nicht gesammelt wurden (wie am besten hier erklärt: http://chrononsystems.com/blog/hidden-evils-of-javas-stringsplit-and-stringr). "Schade, es erklärt nicht, was Sie behaupten. Es ist auch nicht klar, warum Sie Ihre Zeichenfolge aufteilen möchten, anstatt sie zu analysieren." – Tom
Was ist das Kriterium, nach dem Sie die Elemente von 'Token' akzeptieren oder ablehnen? –
Eine andere Lösung besteht darin, die Zeichenfolge nicht zu teilen, sondern die Zeichenfolge in-situ zu scannen/analysieren/zu verarbeiten. –