180 likes | 369 Views
Performance-Optimierung in JAVA. Bernegger Hans Peter Klinger Andreas Schneider Martin. Themen. Geschwindigkeit von Java-Programmen, Interpretierter und kompilierter Code, Tuning von String-Zugriffen, Verwendung von Vektoren und Listen, Tuning von Dateizugriffen,
E N D
Performance-Optimierung in JAVA Bernegger Hans Peter Klinger Andreas Schneider Martin
Themen • Geschwindigkeit von Java-Programmen, • Interpretierter und kompilierter Code, • Tuning von String-Zugriffen, • Verwendung von Vektoren und Listen, • Tuning von Dateizugriffen, • Aspekte der Speicheroptimierung in Java, • Profiling.
Überblick (1) • Charakteristika von Java; • Verarbeitung von Zeichenketten: • Langsame String-Verkettung, • Optimierte String-Verkettung, • Weitere String-Operationen, • StringBuffer-Methode toString(); • Vektoren und Listen: • Die Klasse java.util.Vector,
Überblick (2) • ArrayList und LinkedList; • Dateizugriffe; • Speicheroptimierung; • Profiling: • Profiling-Grundlagen, • Einsatz eines Profilers; • Allgemeine Verbesserungen.
Charakteristika von Java • Plattformunabhängigkeit; • Just-In-Time-Compiler (JIT): • Schnellere Ausführung von Methoden; • Performance-Einbußen durch Checks bei • Bereichsüberschreitungen bei (Arrays, Strings), • Dereferenzierung von Zeigern gegen null, • Zuweisungen von Objektinstanzen, • vielen arithmetischen Operationen.
Langsame String-Verkettung • Sample-Java-Code: String s = ""; for (int i=0; i<20000; i++) s += “*"; (1) • Interne Übersetzung des Compilers: s = new StringBuffer(s). append("*").toString(); (1*) • 60000 temporäre Objekte, wdh. Kopieren der Zeichen im StringBuffer-Konstruktor.
Optimierte String-Verkettung • Sample-Java-Code: StringBuffer b = new StringBuffer(1000); for (int i=0; i<20000; i++) b.append(“*“); (2) String s = b.toString(); • Vorteile: • Aufruf von (2) meist in konstanter Zeit, • Faktor 10 schneller, • 60000 temporäre Objekte weniger.
Weitere String-Operationen • Einfügen in Strings: • s = "*" + s; • b.insert(0, "*"); • Faktor 4 und keine temporären Objekte. • Löschen in Strings: • s = s.substring(0, 1000) + s.substring(2000); • s = b.delete(1000, 2000).toString(); • Löschen anstelle von Verbinden.
StringBuffer-Methode toString() • Kein Kopiervorgang beim Aufruf, • derselbe verwendete Puffer, • shared-Flag im StringBuffer-Objekt, • Abfrage bei verändernden StringBuffer-Methoden • und ev. Kopieren vor der Veränderung; • echter Kopiervorgang erst nötig bei schreibendem Zugriff auf den StringBuffer.
Die Klasse java.util.Vector • Vector: • Speicherung von Listen von Objekten. • Sequentieller und wahlfreier Zugriff. • Implementierung des Datenpuffers als Array. • Attribute Kapazität und Ladefaktor. • Standard: Kapazität=10, Ladefaktor=0 (=2*). • Auch ohne Multithreading-Betrieb infolge von synchronized-Methoden entsprechend langsam.
LinkedList und ArrayList • LinkedList: • Implementierung als doppelt verkettete Liste. • Keine Kopiervorgänge beim Erweitern. • Vielzahl der allozierten Objekte. • ArrayList: • Implementierung ähnlich wie Vector. • Infolge fehlender synchronized-Attribute sehr hohe Performance.
Dateizugriffe (1) • Writer-Klassen: • Verarbeitung von Character-Streams. • 16-Bit breite UNICODE-Zeichen. • Kapselung in einen BufferedWriter. • Meisten externen Dateien arbeiten mit 8-Bit-Zeichen Konvertierung durch Methoden der Klasse sun.io.CharToByteConverter.
Dateizugriffe (2) • OutputStream-Klassen: • Arbeiten mit Bytes anstelle von Zeichen. • Keine aufwendige Konvertierung. • Ausgabe der niederwertigen 8 Bit je Zeichen. • RandomAccessFile: • Ungepufferter Byteweiser Zugriff langsam. • Verarbeitung eines ganzen Arrays von Bytes. • int num = file.read(buf);
Speicheroptimierung • Jede Allozierung kostet Rechnerzeit. • Objekte recyceln. • Nicht mehr referenzierter Speicher. • Große Mengen an temporären Objekten. • Objektreferenzen OutOfMemoryError. • System.gc(); • Variablen explizit Wert null zuweisen.
Profiling-Grundlagen • Überwachung und Messung: • Performanceparameter (z.B. CPU-Zeit), • Anzahl der allozierten Objekte, • Anzahl der Aufrufe bestimmter Methoden. • Profiler-API: JVMPI. • Beispielimplementierung hprof: • Brauchbares, • aber rein textuelles Analyse-Tool.
Einsatz eines Profilers • Programm mit Option –Xrunhproof starten. • Größen Rechenzeit- und Speicherverbrau-cher werden ermittelt, und über ihre Stack-traces wird folg. in LOG-File geschrieben: • Woher werden sie aufgerufen? • Wie tragen sie zum Programmergebnis bei? • Programm kann verbessert werden. • Zufriedenstellend oder erneut von vorne.
Allgemeine Verbesserungen (1) • Hoher Anteil von lokalem Code am Rechenzeitverbrauch: • Vermindern der Anzahl von Schleifendurchläufen, • Verwendung von Ganzzahlen, • Herausziehen von schleifeninvariantem Code, • Vermeiden der Doppelauswertung von gemeinsamen Teilausdrücken, • Wiederverwenden bekannter Teilergebnisse,
Allgemeine Verbesserungen (2) • Verwendung alternativer Datenstrukturen, • Eliminieren von unbenutztem Code, • Reduzieren der Stärke von Ausdrücken. • Hoher Anteil in Untermethoden: • Verminderung der Aufrufhäufigkeit, • Ersetzung durch performantere Aufrufe, • Erhöhung der Ablaufgeschwindigkeit. • System.currentTimeMillis();