Da Processing wahrscheinlich eine angepasste Version von Java benutzt (da dort z. B. auch Code außerhalb von Klassen erlaubt ist) habe ich den StringBuilder-Vergleich nun auch für Eclipse-Java angepasst – man vergleiche mit dem vorherigen Beitrag für Processing/Java.
Voraussetzung für das erfolgreiche Durchführen war den Java-Heap Speicher in Run Configurations unter VM arguments anzupassen - aber nur die maximale Größe mit dem –Xmx-Parameter, mit dem –Xms-Parameter für die initiale/anfängliche Größe wäre der RAM-Vergleich nicht mehr korrekt.
Die Methode loadStrings() scheint Processing- und nicht Java-spezifisch zu sein, ich habe daher die letzte Lade-Variante durch die FileUtils.readFileToString-Methode der frei verfügbaren Commons IO-Bibliothek von Apache ersetzt. Gemäß diesem Link gibt es in den Standard-Java-Bibliotheken keine Möglichkeit um direkt in einen einzelnen String zu laden.
Insgesamt sind die Ergebnisse aber vergleichbar mit Processing/Java, ich wollte nur den Vergleich korrekt ausführen. Der StringBuilder in Java benötigt definitiv zusätzlichen RAM bei der .toString()-Methode, das Äquivalent im .NET-Framework dagegen nicht.
Damit der Java-Prozess nicht sofort wieder geschlossen wird lasse ich das Programm abschließend auf einen Tastendruck warten.
Dateigröße auf Festplatte: 65784159
1.1) zeilenweises Einlesen mit nötigem Overhead:
Dateigröße im RAM: 65784171
Overhead: 12 Zeichen
1390 ms Zeit benötigt zum Datei-Laden.
RAM-Verbrauch: 175 MB.
1.2) zeilenweises Einlesen ohne die nötigen 12 Zeichen Overhead:
Dateigröße im RAM: 65784171
Overhead: 12 Zeichen
2021 ms Zeit benötigt zum Datei-Laden.
RAM-Verbrauch: 425 MB.
2) zeichenweises Einlesen:
Dateigröße im RAM: 65784159
5313 ms Zeit benötigt zum Datei-Laden.
RAM-Verbrauch: 125 MB.
3) in 1000-Zeichen-Blöcken einlesen:
Dateigröße im RAM: 65784159
964 ms Zeit benötigt zum Datei-Laden.
RAM-Verbrauch: 160 MB.
4) Laden mittels FileUtils der Apache-Bibliothek org.apache.commons.io:
Dateigröße im RAM: 65784159
Overhead: 0 Zeichen
1946 ms Zeit benötigt zum Datei-Laden.
RAM-Verbrauch: 260 MB.
Update 16.12.2011: Im Internet hat man mir die JConsole für das Monitoring von Java-Anwendungen empfohlen, sowie die Seite Java Performance Tuning. Die bisher erzielten Rückschlüsse sind für mich aber bereits ausreichend.
Update 17.01.2012: Den append-Befehl vom Stringbuilder gibt es überladen auch in dieser Variante: append(char[] str, int offset, int len)
. Dadurch kann man sich beim blockweisen Lesen den Rest nach der Schleife einsparen:
while ((buffer = br.read(ioBuf,0,1000)) != -1)
{
/* eingelesene Zeile anhängen */
result.append(ioBuf,0,buffer);
}
Eine Amerkung noch zum Quellcode - leider markiert BlogEngine.NET nach // häufig auch weitere nachfolgende Zeilen als Kommentarzeilen.
import java.lang.Math;
import java.util.concurrent.TimeUnit;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import org.apache.commons.io.FileUtils;
public class Program
{
public String test = "";
public int stringBuilderOverhead = 12;
public int fileLength = 0;
// systemeigenes Zeilenumbruchszeichen ermitteln
String sep = System.getProperty("line.separator");
public String loadPath="C:/Users/thomas/Downloads/pg2600_2.txt";
public StringBuilder completeString=new StringBuilder(0);
/*-----------------------------------------------------------------------------
* Variante 1: zeilenweises Einlesen einer Textdatei mit StringBuilder-Overhead
*-----------------------------------------------------------------------------*/
void loadFile1()
{
int overhead = 0;
/* nimm die Vorher-Zeit für Gesamtdurchlauf */
long completeTimeBefore = System.currentTimeMillis();
completeString = new StringBuilder(fileLength + stringBuilderOverhead);
try
{
String thisLine=null;
BufferedReader br = new BufferedReader(new FileReader(loadPath));
while ((thisLine = br.readLine()) != null)
{
/* eingelesene Zeile anhängen */
completeString.append(thisLine);
/* Zeilenumbruch hinzufügen */
completeString.append(sep);
}
br.close();
} catch (IOException e) {
System.out.println("Datei konnte nicht eingelesen werden!");
return;
}
System.out.println("Dateigröße im RAM: " + (overhead=completeString.length()));
System.out.println("Overhead: " + (overhead-fileLength) + " Zeichen");
/* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */
long completeTimeAfter = System.currentTimeMillis();
long completeTimeDiff = completeTimeAfter - completeTimeBefore;
System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n");
}
/*-----------------------------------------------------------------------------
* Variante 2: zeichenweises Einlesen und zeichenweises Hinzufügen
*-----------------------------------------------------------------------------*/
void loadFile2()
{
/* nimm die Vorher-Zeit für Gesamtdurchlauf */
long completeTimeBefore = System.currentTimeMillis();
completeString = new StringBuilder(fileLength);
try
{
int thisChar;
BufferedReader br = new BufferedReader(new FileReader(loadPath));
while ((thisChar = br.read()) != -1)
{
/* eingelesene Zeile anhängen */
completeString.append((char) thisChar);
}
br.close();
} catch (IOException e)
{
System.out.println("Datei konnte nicht eingelesen werden!");
return;
}
System.out.println("Dateigröße im RAM: " + completeString.length());
/* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */
long completeTimeAfter = System.currentTimeMillis();
long completeTimeDiff = completeTimeAfter - completeTimeBefore;
System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n");
}
/*-----------------------------------------------------------------------------
* Variante 3: in 1000-Zeichen-Blöcken einlesen und hinzufügen
*-----------------------------------------------------------------------------*/
void loadFile3()
{
/* nimm die Vorher-Zeit für Gesamtdurchlauf */
long completeTimeBefore = System.currentTimeMillis();
completeString = new StringBuilder(fileLength);
try
{
char[] ioBuf = new char[1000];
/* buffer liefer Anzahl gelesener Zeichen zurück, siehe auch
http://www.dpunkt.de/java/Referenz/Das_Paket_java.io/4.html#read%28%29 */
int buffer = 0;
BufferedReader br = new BufferedReader(new FileReader(loadPath));
while ((buffer = br.read(ioBuf,0,1000)) == 1000)
{
/* eingelesene Zeile anhängen */
completeString.append(ioBuf);
/* Array wieder leeren falls nur weniger Zeichen eingelesen werden können */
ioBuf = new char[1000];
}
/* die letzten gelesenen Zeichen müssen auch hinzugefügt werden */
/* -1 wäre wenn von vornherein gar kein Zeichen eingelesen werden konnte */
if (buffer != -1)
{
for (int i=0;i<buffer;i++)
completeString.append(ioBuf[i]);
}
br.close();
} catch (IOException e)
{
System.out.println("Datei konnte nicht eingelesen werden!");
return;
}
System.out.println("Dateigröße im RAM: " + completeString.length());
/* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */
long completeTimeAfter = System.currentTimeMillis();
long completeTimeDiff = completeTimeAfter - completeTimeBefore;
System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n");
}
/*-----------------------------------------------------------------------------
* Variante 4: Laden mittels FileUtils der Apache-Bibliothek org.apache.commons.io
*-----------------------------------------------------------------------------*/
void loadFile4()
{
int overhead=0;
/* nimm die Vorher-Zeit für Gesamtdurchlauf */
long completeTimeBefore = System.currentTimeMillis();
try
{
/* eigentliches Einlesen */
/* readFileToString gibt es nicht direkt in Java, siehe auch
* http://stackoverflow.com/questions/3849692/whole-text-file-to-a-string-in-java
*/
File currentFile = new File(loadPath);
completeString.append(FileUtils.readFileToString(currentFile, "utf-8"));
currentFile = null;
} catch (IOException e)
{
System.out.println("Datei konnte nicht eingelesen werden!");
return;
}
System.out.println("Dateigröße im RAM: " + (overhead=completeString.length()));
System.out.println("Overhead: " + (overhead-fileLength) + " Zeichen");
/* nimm die Danach-Zeit für Gesamtdurchlauf und bestimme Differenz */
long completeTimeAfter = System.currentTimeMillis();
long completeTimeDiff = completeTimeAfter - completeTimeBefore;
System.out.println(completeTimeDiff + " ms Zeit benötigt zum Datei-Laden.\n");
}
public static void main(String[] args)
{
Program test = new Program();
File file = new File(test.loadPath);
test.fileLength = (int) file.length();
file = null;
System.out.println("Dateigröße auf Festplatte: " + test.fileLength);
//System.out.println("\nzeilenweises Einlesen: ");
//test.loadFile1();
//System.out.println("\nzeichenweises Einlesen: ");
//test.loadFile2();
//System.out.println("\nin 1000-Zeichen-Blöcken einlesen: ");
//test.loadFile3();
//test.test=test.completeString.toString();
System.out.println("\nLaden mittels FileUtils der Apache-Bibliothek org.apache.commons.io: ");
test.loadFile4();
// Datei für Diff-Vergleich schreiben
/*try
{
FileWriter f1 = new FileWriter("C:/Users/thomas/Downloads/pg2600_2_2.txt");
f1.write(test.completeString.toString());
f1.close();
} catch (IOException e) {
System.out.println("Datei konnte nicht geschrieben werden!");
} */
System.out.println("Enter drücken um zu beenden.");
try {
int LV_IntAbort = System.in.read();
} catch (IOException e) {
System.out.println("FEHLER >>> " + e);
}
}
}