Make Java - Performance

Teil 3
System und Umgebung


Nun wollen wir uns dem ersten Teil der Optimierung von System und Umgebung widmen. Die Aussagen werden dabei auch andere Teile dieses Dokuments streifen. Damit Sie nicht unnötig sich durch das Dokument suchen müssen, wird jeder Punkt jedoch so ausführlich wie hier notwendig betrachtet.

Vor- und Nachteile

Der wesentliche Vorteil einer Optimierung von System und Umgebung liegt in der Tatsache begründet, dass die Optimierung keine oder nur sich nur gering auf die Implementierung und das Design Ihrer Anwendung auswirkt. Leider ist dies nicht ganz so optimal wie es erst einmal den Anschein hat. Eine andere Datenbank kann schon erhebliche Auswirkungen auf die Implementierung haben. selbst ein Wechsel des Datenbanktreibertyps kann uns nicht zwingend davor schützen.
Der große Nachteil der folgenden Optimierungen besteht in der Tatsache, dass die Optimierungen spezifisch für das jeweilige System vorgenommen werden müssen. Haben Sie nur 64 MB Arbeitsspeicher können / sollten Sie den Umgebungsspeicher für die JVM nicht auf 128 MB setzen. Für Ihre privaten Tools lassen sich jedoch hier kaum Einschränkungen machen, sofern diese nur auf einem Rechner laufen.

Die richtige Java Virtual Machine [JVM]

Das JVM nicht gleich JVM ist, ist allgemein bekannt. Dies liegt natürlich auch an den JIT Compiler und der neueren Version dem Hot Spot. Was aber bedeutet die richtige JVM zu wählen. Die richtige JVM bedeutet vielmehr die richtige JRE und bei der Entwicklung das richtige JDK zu benutzen. Neben der Quelle Sun Microsystem ist IBM die große Alternative für verschiedene Plattformen. Es gibt jedoch auch weitere Anbieter und ein Vergleich lohnt sich. So bedenken sind jedoch einige wichtige Punkte. Sun Microsystem setzt den Standart und ist immer aktuell. Die Alternativen ermöglichen meist eine schnellere Ablaufgeschwindigkeit, liegen jedoch nach einer neuen Version des JDKs einige Monate im Rückstand. Beachten Sie auch, dass Sie niemanden vorschreiben können, welche JVM er benutzen soll. Ziel sollte somit stets sein, dass Ihre Anwendung zumindest unter der Sun JVM performant genug läuft. Ein weiterer wichtiger Punkt ist, dass nicht jede Alternative das gesamte Klassenspektrum unterstützt. So gibt es beispielsweise spezielle JVMs für den Serverbereich, welche weder AWT noch Swing unterstützen. Der Einsatz der IBM Implementation der JVM bringt auf meinem System Geschwindigkeitsvorteile von bis zu knapp 200%.
Ein weiterer wichtiger Punkt ist die JVM in den Internetbrowsern. Hier ist die Geschwindigkeit nicht nur vom jeweiligen Browser, sondern auch vom dahinterliegenden Betriebssystem abhängig. Im Internet lassen sich dazu stets einige Vergleiche finden:
BetriebssystemBrowserGeschwindigkeit
Win 2000IE 5.0172.944
Win 98IE 5.0172.590
Win 98Navigator 4.7360.565
Win 2000Navigator 4.7360.123
Win 2000Opera 4 Beta 657.736
Win 98Opera 4 Beta 657.294
Win NT 4IE 5.0149.160
Win NT 4Navigator 4.7344.873
Win NT 4Opera 4 Beta 637.754
Suse Linux 6.4Navigator 4.738.795

Tabelle 1 - Testwerte von zd-Bench 1.5 Quelle: http://www.zdnet.de
(Höhere Werte bedeuten bessere Performance.)
Bytecode Interpreter
Der Urahn der JVM arbeitete mit dem Bytecode Interpreter und auch die heutigen Virtuellen Maschinen lassen sich immer noch in den Interpretermodus umschalten. Dieser Modus erleichtert insbesondere die Fehlersuche und wird daher von Debuggern gern verwendet. Der Bytecode Interpreter arbeitet die einzelnen Befehle im Javabytecode wie bei einer Scriptsprache ab. Der jeweilige Befehl wird eingelesen in Maschinencode umgesetzt und ausgeführt. Da dies für jeden Befehl auch bei nochmaligem Aufruf neu geschieht, hält sich die Ausführungsgeschwindigkeit in Grenzen. Sie ermöglichen keine Nutzung der Maschinenarchitektur, wie Cache und Sprungvorhersage. Bytecode Interpreter sind jedoch die Mindestumsetzung einer JVM und daher auf allen Javaplattformen vorhanden. Bytecode Interpreter waren die ersten JVM und sind daher auch weit verbreitet. Heutzutage ist jedoch grundsätzlich zumindest mit JIT Compilern zu arbeiten, um auch den gestiegenen Performancebewusstsein gerecht zu werden.
Just-In-Time Compiler
Just In Time [JIT] Compiler sind die Nachfolger der Bytecode Interpreter. Sie übersetzen den gesamten Bytecode vor Ausführung in Maschinencode. Wenn dies geschehen ist wird lediglich auf den Maschinencode zurückgegriffen. Durch die Übersetzung des Bytecode erreichen JIT Compiler eine wesentlich höhere Ausführungsgeschwindigkeit. Ziel der JIT Compiler ist es dabei den Maschinencode möglichst schnell, nicht jedoch möglichst schnellen Maschinencode zu erstellen. JIT Compiler übersetzen grundsätzlich den gesamten zur Verfügung stehenden Bytecode. Dies hat zur Folge, dass JIT Compiler einen sehr hohen Speicherplatzbedarf haben. JIT Compiler waren für die Ausführungsgeschwindigkeit von Javaanwendungen ein enormer Sprung. Sie können jedoch nicht den einmal erzeugten Maschinencode weiter optimieren. Hier kommt die nächste Generation der HotSpot Compiler zum tragen.
Hot Spot Compiler
HotSpot Compiler sind erweiterte JIT Compiler. Sie haben zusätzlich zum JIT Compiler die Möglichkeit den Maschinencode zu Überwachen und ggf. eine optimierte Neuübersetzung des Maschinencode zu veranlassen. In diesem Zusammenhang arbeitet der HotSpot Compiler wie ein Profiler, welcher die Auswertung und Änderung des geprüften Anwendungsteils übernimmt. Zudem hat der HotSpot Compiler eine altersbasierende Garbage Collection. D.h. während kurzlebige Objekte schneller aus dem Speicher entfernt werden bleiben länger und langlebige Objekte im Speicher. Dies ermöglich dem HotSpot Compiler derartige Objekte schneller zu erzeugen. Durch die Tatsache, dass der HotSpot Compiler den bereits erstellten Bytecode ständig überprüft und ggf. neu übersetzt, ist er statischen / nativen Compilern in diesem Punkt überlegen. Sun Microsystems hat den HotSpot Compiler in zwei Versionen entwickelt, die Client-JVM und die Server-JVM. Bei der Clientversion steht weiterhin die Geschwindigkeit der Übersetzung und, anders als beim JIT Compiler, des Starten der Anwendung im Vordergrund. Die Client-JVM startet hierbei die Javaanwendung wie ein Bytecodeinterpreter und übersetzt während der Ausführung der Anwendung die zeitkritischen Anwendungsteile. Auch hier gilt, dass der Maschinencode zuerst nicht nach Performancegesichtspunkten entsteht. Erst durch das integrierte Profiling entsteht ein schneller Maschinencode. Anders jedoch die Servervariante. Hier wird der zeitkritische Bytecode bereits vor dem Starten der eigentlichen Anwendung in optimierten Maschinencode übersetzt. Erst dann wird die Anwendung gestartet. Dies ist sinnvoll, da Serveranwendung normalerweise eine wesentliche längere Ausführungsdauer haben als Stand Alone Anwendungen oder Applets.
Es lässt sich letztendlich folgendes feststellen:
Bytecode Interpreter sind langsam, jedoch stets vorhanden. Sie benötigen die geringsten Systemressourcen, da Sie jeweils nur den aktuellen Befehl übersetzen. JIT Compiler sind Standard uns sollten nach Möglichkeit verwendet werden. Sie erreichen hohe Ausführungsgeschwindigkeiten, fordern jedoch für diese auch hohe Systemressourcen. HotSpot Compiler vereinen die Vorteile von Bytecode Interpreter (sofortiger Start, wenig Systemressourcenanspruch) mit denen von JIT Compilern (schnelle Ausführungsgeschwindigkeit). Sie haben Vorteile gegenüber statischem / nativem Compilieren und sollten die erste Wahl sein. Sie sind jedoch nicht auf allen Plattformen verfügbar.
Thread-Umsetzung
Wenn Sie ein Mehrprozessorsystem Ihr Eigen nennen, müssen Sie noch einen weiteren wichtigen Punkt beachten: Die Umsetzung der Threads in das Betriebssystem.
Java ermöglicht mit seiner Klasse Thread und dem Interface Runnable nebenläufige Programmteile zu erstellen. Dies allein kann schon zur subjektiven Performancesteigerung führen. Sofern Sie ein System mit mehreren Prozessoren haben, ist die Umsetzung dieser Threads auf das Betriebssystem wichtig. Unterschieden werden dabei drei Typen. Der Typ „Many to One“ bedeutet, dass alle Java Threads in einem Betriebsystem Thread umgesetzt werden. Dies bedeutet auch, dass Sie nur einen Ihrer Prozessoren nutzen können und sollte daher bei Mehrprozessorsystemen keine Verwendung finden.
Der zweite Typ „One to One“ legt für jeden Java Thread zwingend einen Betriebssystem Thread an. Obwohl dies auf den ersten Blick vorteilhaft scheint, müssen Sie beachten, dass das Erzeugen eines Threads weder in Java noch auf Betriebssystemebene ein performanter und schneller Vorgang ist. Viele kleine Threads können hier Ihre Anwendung letztlich mehr behindern als beschleunigen.
Der dritte Typ „Many to Many“ ermöglicht schließlich der JVM zu entscheiden, ob für einen Java Thread auch ein Betriebssystem Thread angelegt werden soll. Dieser Typ ist generell für Mehrprozessorsystem zu empfehlen.

Hardware

Prozessoren
Inzwischen gibt es verschiedene Hersteller, welche sich mit der Herstellung von Hardware beschäftigen, die den Java Bytecode direkt ausführen können oder für diesen zumindest optimiert sind. Aufgrund der relativ hohen Kosten rentiert sich der Einsatz dieser jedoch nur selten. Meist ist es kostengünstiger und effektiver seine Finanzen in die Verbessung des Systems allgemein zu investieren (Prozessor, Netzbandbreite, etc.). Dies führt neben der Beschleunigung von Javaanwendungen auch zu einer besseren Performance der nativen Anwendungen, wie beispielsweise das Betriebssystem.
Grafikausgabe
Ein bisschen Hintergrundwissen noch zur grafischen Ausgabe. Natürlich ist Java nicht direkt unabhängig vom Betriebssystem. Mit einer guten DirectX- und OpenGL-Grafikkarte kann man Swing und den Java2/3D-APIs unter Windowssystem gut beschleunigen.

Native Übersetzung

Auch wenn ich selbst kein Freund von nativen / statischen Compilern bin, sollen diese nicht fehlen. Native Compiler übersetzen den Javaquelltext direkt in Maschinencode. Die zweite Möglichkeit ist das Postcompiling. Hierbei wird der Java Bytecode in Maschinencode übersetzt. Dieses Verfahren wird für Serveranwendungen häufig verwendet, da somit eine plattformunabhängige Anwendung erstellt wird, welche nur ggf. für bestimmte Server in den grds. schnelleren Maschinencode übersetzt werden kann. Der Vorteil der nativen Übersetzung liegt u.a. in der Geschwindigkeit. Native Anwendungen sind (sollten) schneller als Bytecode Interpreter und JIT Compiler. Der Start einer nativen Anwendung erfolgt immer schneller als der Start über eine JVM. Hier entfällt bereits das Übersetzten des Bytecode in Maschinencode. Nativer Compiler können sich mit der Übersetzung beliebig viel Zeit lassen, da diese nur einmal anfällt. Dadurch ist es möglich besonders schnellen Maschinencode zu erstellen.
Es gibt jedoch auch Nachteile. Der Maschinencode ist jeweils Betriebssystemabhängig und damit geht eine der wichtigsten Eigenschaften von Java, die Plattformunabhängigkeit, verloren. Außerdem ist das nachträgliche Einbinden von Javaklassen nicht mehr möglich. Hier gilt das Prinzip alles oder nichts. Der HotSpot Compiler greift die nativen Compiler noch mit der Möglichkeit des Ändern des Maschinencode zur Laufzeit an. Hier sind nativer Compiler klar unterlegen. Der Maschinencode wird immer aufgrund der jetzigen und wahrscheinlichen Systemkonfiguration zu Laufzeit übersetzt und kann Änderungen somit nicht berücksichtigen. Es gibt zahlreiche Projekte und Firmen, welche sich die native Übersetzung zum Ziel gemacht haben, so dass Sie mit einer Suche im Internet schnell Erfolg haben werden.

Speicher

Die wahrscheinlich einfachste Möglichkeit eine Geschwindigkeitssteigerung zu erreichen ist die Erhöhung des Umgebungsspeichers für die JVM. Natürlich leider die Performance in Bezug auf Speicherverbrauch darunter aber dies meist nur bei Servern ein Problem. Tastsächlich läuft die JVM nicht schneller sondern ein Teil, die Garbage Collection, muss seltener auf Hochtouren laufen. Dazu sollten wir uns kurz mit der Garbage Collection beschäftigen. Java hat eine automatische Speicherbereinigung. Das bedeutet als Entwickler müssen Sie sich eigentlich keine Sorge um die Müllverwertung, das Entfernen von überflüssigen Objekten machen. Diesen Mechanismus nimmt die Garbage Collection war. Die meisten Garbage Collections arbeiten als Low Priority Thread im Hintergrund. Sofern kein Speicher mehr vorhanden ist, arbeitet diese jedoch mit höherer Priorität als Ihre Anwendung. Dies bedeutet, dass wenn Ihre Anwendung häufig Speichermangel hat, die Garbage Collection die Geschwindigkeit deutlich absinken lässt. Die Erhöhung des Umgebungsspeichers kann dies hinauszögern. Eine andere Möglichkeit ist die Garbage Collection anzuweisen mit den Speicher nur dann zu bereinigen, wenn kein Speicher mehr vorhanden ist. Dies bietet sich insbesondere bei kurzlebigen Anwendungen an. Dieser Vorteil ist jedoch relativ. Wenn die Garbage Collection auf Grund von Speichermangel läuft, prüft Sie stets alle Objekte im Speicher, ob diese freigegeben werden können. Durch geeignete Implementierung kann dies jedoch vermieden werden. Objekte die eine Referenz auf null haben, benötigen keine Prüfung und werden daher übersprungen. Sofern Sie Objekte definitiv nicht mehr benötigen sollten Sie daher die Referenz auf null setzen. Weitere probate Mittel ist der Einsatz von Objektpools und Cacheverfahren. Die Parameter für die JVM sind leider je nach verwendeter JVM und Version unterschiedlich. Zum Prüfen stellt die JVM von Sun den Parameter -verbose gc bereit. Die JVM gibt Ihnen somit den jeweiligen Speicherverbrauch an, so dass Sie leicht feststellen können, ob die Zuweisung von mehr Arbeitsspeicher sinnvoll ist. Der Parameter -noasngc ermöglicht es Ihnen bei der Sun JVM schließlich die Arbeit der Garbage Collection im Hintergrund auszuschalten.

Tools

Das JDK wird bereits mit einigen Tools ausgeliefert. Mit dem richtigen Einsatz der verschiedenen Tools lassen sich bereits Performancegewinne erzielen.
javac
Der Compiler javac ermöglicht uns bereits einfache Optimierungsmöglichkeiten. Da wir den Quelltext hier nicht mehr verändern müssen, sind Optimierungen durch den Compiler Quelltextmanipulationen vorzuziehen.
Automatische Optimierung
Mit Hilfe des Parameters „-o“ wird der Optimierungsmodus bei der Übersetzung in Bytecode eingeschaltet. Dieser kann sowohl Geschwindigkeitsgewinne als auch Gewinne in der Größe des Bytecode bringen. Der Compiler unterstützt dabei unter anderem das Methodeninlining und entfernt nicht erreichbaren Quelltext. Inlining bedeutet, dass private Methoden und final Attribute nicht aufgerufen bzw. deren Wert abgefragt wird, sondern dass der Bytecode bzw. Wert direkt an die verwendeten Stellen kopiert werden. Dies führt zwar zu mehr Bytecode, erhöht jedoch auch die Geschwindigkeit, da der Overhead eines Methodenaufrufs nicht zum Tragen kommt. Ein weiterer Vorteil liegt an der Tatsache, dass Sie nicht per Hand das Inlining durchführen. Dadurch bleibt die Wartbarkeit des Quelltextes erhalten - Sie müssen spätere Änderungen weiter nur an einer Stelle vornehmen. Das automatische Inlining findet jedoch nur bei relativ kurzen Methoden statt.
Es werden jedoch nicht alle bekannten Optimierungsmöglichkeiten genutzt. Beispielsweise werden überflüssige Kopieranweisungen, wie
// hier stand noch etwas
a = h;
o = h;
a = o;
// hier folgt noch etwas

oder auch überflüssige Zuweisungen, wie
// hier stand noch etwas
u = u++;
u = 25;
// hier folgt noch etwas

bisher nicht optimiert.
Debug Informationen
Der Compiler „javac“ speichert neben dem reinen Bytecode auch noch Metadaten in der Klassendatei. Hierbei werden u.a. die Zeilennummern und Variablennamen gesichert. Dies führt natürlich zu entsprechend größeren Bytecodedateien. Sie können jedoch dem Abhilfe schaffen. Durch Verwendung des Parameters „-g:none“ können Sie diese Metadaten entfernen. Nach einem abgeschlossenen Debugging können Sie somit den Bytecode weiter verkleinern. Die Klasse World können Sie mit den Parametern des Javacompilers so wesentlich Verkleinern.
class World{
  public static void main (String [] arg){
    if (true){
      System.out.println (“Hello World”);
    }
  }
}

ParameterGröße des BytecodeRelativer Zeitverbrauch für die Compilierung
415 Bytes880
-g:none -O335 Bytes940
Tabelle 2 - Optimierungswerte des Standard Javacompilers
Alternative Compiler
Alternative Compiler bieten gerade in bezug auf die Performance weitere Steigerungsmöglichkeiten. Sie analysieren den Quelltext auf weitere Punkte und optimieren so den Bytecode. Auch Obsfucators führen nicht selten noch Performanceoptimierungen durch. Einer der bekanntesten Compiler ist „jikes“. Wie immer haben die Alternativen jedoch das Problem der Aktualität. Alle im Dokument gemachten Aussagen beziehen sich daher auf den Standardjavacompiler.
serialver
Mit Hilfe des Tools „serialver“ (Serial Version Inspector), welches mit dem JDK ausgeliefert wird, können Sie die Serialversion einer Klasse berechnen. Das direkte Einfügen dieser in serialisierbare Klassen bringt einen geringen Geschwindig­keitsvorteil. Mit Hilfe des Startparameters ‚-show’ können Sie die grafische Oberfläche des Tools starten. Durch Eingabe des qualifizierten Klassennamen erhalten Sie dann die serialVersionUID, welche Sie lediglich noch in Ihre Klasse kopieren müssen.

Statt mit der grafischen Oberfläche zu arbeiten können Sie auch direkt den qualifizierten Klassennamen als Startparameter übergeben.
jar
Mit dem jar Packer hat Sun einen weiteren Schritt unternommen, um die Performance zu erhöhen. Gerade bei Netzwerkverbindungen mit niedriger Bandbreite bietet sich die Verwendung von jar Paketen an. Dies ist insbesondere in Verbindung mit Applets vorteilhaft. jar selbst ist ein Packer, welcher auf dem zip Algorithmus beruht. Zusätzlich kann ein jar Paket noch Metainformationen, wie zum Beispiel die Startklasse enthalten. Der Einsatz der jar Pakete hat sich inzwischen auch bei Javaanwendungen durchgesetzt. Dies hat nicht zuletzt etwas mit der Benutzerfreundlichkeit zu tun. Mit ein paar kleinen Handgriffen kann man jar Dateien auch unter Windows zur selbststartende Datei machen.
Obsfucators
Die eigentliche Aufgabe von Obsfucator Tools ist es den Bytecode vor unberechtigtem Einsehen und Dekompilierung zu schützen. Als nützlicher Nebeneffekt wird der Bytecode meist noch wesentlich verkleinert. Realisiert wird dieses, in dem Methoden und Variablennamen durch kurze Zeichenketten ersetzt werden. So kann aus einem „gibAlter(Person p)“ schnell ein „d(P a)“ werden. Dabei erkennen die Tools, ob es sich um eine externe Klasse oder um eine mit zu optimierende Klasse handelt. Einige Obsfucatoren nehmen außerdem noch Bytecodeänderungen zur Optimierung der Ausführungsgeschwindigkeit vor. Hier ist wie immer natürlich darauf zu achten, dass auch 100% Pure Java Bytecode erstellt wird. Wie vieles hat sich auch hier schon die Open Source Gemeinde an die Arbeit gemacht. Der Obsfucator RetroGuard kann z.B. die jar Datei „swingall.jar“ von 2.420.388 Byte auf 1.737.944 Byte drücken, was etwa 30 % ausmacht. Obsfucators eignen sich jedoch nur, wenn Sie ein fertiges Produkt ausliefern. Wollen Sie ein Bean oder eine Klassenbibliothek anbieten, werden Sie mit Klassennamen wie A und B wohl keinen Erfolg haben J.
Profiler
Damit Sie eine Anwendung performanter gestalten können, ist es nötig zu wissen, wo der Flaschenhals sich befindet. Profiler sind Anwendungen, welche Ihnen erlauben diese Stellen in Ihrer Anwendung zu finden. Sun Microsystems hat auch hier dem Entwickler bereits an Werkzeug an die Hand gegeben. Die JVM verfügt über einen internen Profiler, welchen Sie lediglich starten müssen. Natürlich hat sich auch hier der Parameter in den laufenden Versionen des JDK geändert.
JDK VersionParameter
JDK 1.1.x final-prof
JDK 1.2.x final-Xrunhprof
JDK 1.3.x final-Xrunhprof
Die Ausgabe erfolgt in der Datei „java.prof“ und kann mit einem beliebigen ASCII-Editor gelesen werden. Da die Ausgabe an sich jedoch eher kryptisch ist, sollten Sie sich ggf. eine kostenlose Anzeigeanwendung besorgen. Allgemein gern genannt werden hier die Anwendungen „HyperProf“ und „ProfileViewer“. Als weitere Alternative stehen auch kommerzielle Profiletools zur Verfügung.
Profilertools helfen jedoch nur, wenn Sie in der Lage sind das Design bzw. den Quelltext zu ändern; zur Vollständigkeit sind diese jedoch hier mit aufgeführt.

Externe Daten

Grafiken
Grafiken werden in den verschiedensten Varianten verwendet. U.a. als Teil der BNO oder auch in Spielen. Es gibt zwei wesentliche Arten von Grafiken. Bitmap Grafik ist eine Darstellungsform, wobei hier für jeden darzustellen Punkt die Informationen wie Farbe gesichert werden. Die andere Grafikart ist die Vektorgrafik. Diese kennt End- und Anfangspunkte bzw. Formeln, nach denen die Erstellung der Grafik geschieht sowie Farben oder auch Farbverläufe. Bitmapgrafiken werden hauptsächlich für die Verwaltung von Fotos verwendet. Vectorgrafiken haben den Vorteil, dass Sie wesentlich genauer und beliebig vergrößerbar sind. Schriftarten sind meist eine Art Vectorgrafik. Außerdem ist nochmals zu unterscheiden zwischen statischen Grafiken und nicht statischen Grafiken, den Animationen. Java unterstützt standardmäßig die Bitmap Grafiktypen ‚GIF‘ und ‚JPEG‘ und seit Java 2 auch ‚PNG‘. Die richtige Wahl des Grafiktyps ist dabei schon eine erste Möglichkeit den Speicherbedarf und somit ggf. auch die Netzwerklast zu minimieren. Ich bin kein Speziallist was Grafiken betrifft aber einige Eigenheiten sollten Ihnen bekannt sein. GIF Grafiken können maximal 256 Farben (8 Bit) darstellen. Für (hochauflösende) Fotos sind Sie daher ungeeignet. Allerdings können GIF Grafiken einen transparenten Hintergrund haben und auch Animationen enthalten. JPEG Grafiken können 24 Bit (True Color) Bilder verwalten. Die Wahl des richtigen Datenformats kann hier die Größe dieser Dateien verringern. Prüfen Sie auch, ob Sie wirklich animierte GIF Grafiken verwenden wollen. Sollten Sie hier Änderungen vornehmen, können Sie den Speicherverbrauch Ihrer Anwendung zu Laufzeit und auch die Netzwerklast stark vermindern. Besonders das ‚PNG‘ Format ist zu empfehlen. Es verbindet die Vorteile von ‚GIF‘ (z.B. Transparenz) und ‚JPEG‘ (photogeeignet) und unterliegt dabei wie ‚JPEG‘ keinen einschränkenden Rechten.
Gerade für Icons und Infobildern bietet es sich an, das Datenformat Ihrer Grafiken zu prüfen. Hier können Sie noch einige Bytes herausholen. Stellen Sie sich auch die Frage, ob Sie wirklich animierte GIF Grafiken benötigen, da sich hier ein besonders großer Performancegewinn erreichen lässt.

Datenbanken

Der Bereich Datenbanken ist ein wichtiger Punkt um die Performance Ihrer Anwendung entscheidend zu beeinflussen. Hierbei müssen wir zusätzlich noch zwischen Datenbank und Datenbanktreiber unterscheiden, wobei wir letzteres zuerst betrachten wollen. Der Datenbankzugriff wird in Java mittels JDBC realisiert. Für den Zugriff über JDBC werden dabei vier Typen von JDBC Treibern unterschieden.
Datenbanktreiber
Typ 1 - Die JDBC ODBC Brücke
Die JDBC-ODBC-Brücke (kurz Brücke) wird meist zum Testen des Datenbankzugriffs verwendet. Sie hat jedoch, auch wenn Sun Microsystem die Brücke nur als geringes strategisches Ziel sieht, ihre Daseinsberechtigung. Der Vorteil den die Brücke zur Verfügung hat, liegt in der Verbreitung von ODBC. ODBC ist auf fast jedem Betriebssystem vorhanden. Da die Brücke lediglich einen ODBC-Syntax erzeugt und diesen an den ODBC-Treiber weitergibt, ermöglicht Ihnen die Brücke somit einen Datenbankzugriff auf einer Vielzahl von Betriebssystemen. Eine weiterer Vorteil ist die (eigentlich) völlige Unabhängigkeit vom jeweiligen Datenbanksystem. In der Implementierung wird kein Datenbank(treiber)spezifischer Quellcode erzeugt. Somit ist es uns jederzeit möglich die zugrundeliegende Datenbank auszutauschen. Hinzu kommt, dass die Performance in Bezug auf den Speicherverbrauch der eigentlichen Java Anwendung praktisch vernachlässigbar ist. Dies liegt an der Tatsache, dass das Paket java.sql Bestandteil der Standard Java API ist. Der gravierenste Nachteil ist jedoch die Performance in Bezug auf die Geschwindigkeit. Durch die häufigen Konvertierungen die bei einem Zugriff auf das Datenbanksystem nötig ist, wird die Geschwindigkeit unser Java Anwendung vermindert. Da dies zu einem extremen Flaschenhals führt, wird die Brücke heutzutage kaum bei professionellen Anwendungen verwendet. Leider erreicht jedoch kein anderer Typ die komplette Datenbankunabhängigkeit. Ein weiterer Nachteil ist das ODBC Management. Abgesehen von Steuerungsprogrammen für Clients (z.B. MS Netmeeting) und ähnlichen Verfahren muss die ODBC Treibereinrichtung manuell und vor Ort durch einen mit entsprechenden Rechten ausgestatteten Benutzer - meist Administrator - durchgeführt werden. Für einen Prototyp bleibt die Brücke jedoch meist erste Wahl.

Abbildung 1 - Konvertierungen bei Datenbankzugriff über die JDBC ODBC Brücke
Typ 2 - Plattformeigene JDBC Treiber
Plattformeigene JDBC Treiber konvertieren die JDBC Aufrufe direkt in die Aufrufe der Client-API der Datenbank. Dabei werden die proprietären Datenbankschnittstellen verwendet. Dies macht Ihre Abfragen wesentlich schneller als mit Hilfe der Typ 1 - Die JDBC ODBC Brücke. Damit der direkte Zugriff auf die Schnittstelle Ihrer Datenbank jedoch möglich wird, ist es nötig eine kleine Brücken-DLL auf jedem Datenbankclient zu installieren. Mit Hilfe der Plattformeigenen JDBC Treiber wird es Ihnen jedoch möglich direkt auf die im Unternehmen vorhandene Mittelware des Datenbankmanagmentsystems zuzugreifen. Sie sollten sich bei einem Einsatz jedoch im klaren sein, dass Sie sich abhängig von dem jeweiligen Datenbankmanagmentsystem machen. Der Wechsel zu einer anderen Middleware / Datenbank zieht bei dieser Variante erhebliche Änderungen im Quelltext nach sich. Da außerdem eine native DLL installiert werden muss, büßen Sie weiterhin die Plattformunabhängigkeit ein; auch die Anbindung über Internet stellt Sie bei dieser Variante von größere Probleme.
Typ 3 - Universelle JDBC Treiber
Universelle JDBC Treiber konvertieren Ihre Datenbankaufrufe in ein datenbankunabhänigiges Netzwerkprotokoll, welches von dem Datenbankmanagmentsystem dann in das datenbankspezifische Protokoll umgewandelt wird. Im Vergleich zu JDBC ODBC Brücke entfällt somit je eine Konvertierung. Der Einsatz von Universellen JDBC Treibern ermöglicht Ihnen grundsätzlich eine völlige Unabhängigkeit von der Datenbank und Systemplattform. Aus diesem Grund können Sie diesen Treiber im Intra- / Internet genauso gut einsetzen wie bei einer Stand Alone Anwendungen. Dabei weist diese Lösung eine wesentlich höhere Performance als JDBC ODBC Brücke auf und sollte dieser auch vorgezogen werden. Universelle JDBC Treiber werden inzwischen sowohl von den Datenbankherstellern als auch von unabhängigen Softwareanbietern angeboten. Dabei unterstützen auch kostenlose Datenbanken den JDBC Standart. Auf der Internetpräsenz von Sun finden Sie eine Liste der jeweiligen Anbieter. Auch bei den Datenbankanbietern können Sie die jeweils aktuelle Treiber finden. Ein wesentlicher Vorteil dieser Treiber ist, dass Sie eine weittestgehende Unabhängigkeit von der Plattform und dem Datenbankmanagmentsystem erreichen können.
Typ 4 - Direkte JDBC Treiber
Der letzte Typ der JDBC Treiber arbeitet schließlich direkt auf der Protokollebene der Datenbank. Durch diese Tatsache ist dieser Typ aus Performancesicht eindeutig zu bevorzugen, da ein Client direkt auf den Datenbankserver, ohne den Umweg über die Middleware, zugreifen kann. Da die Schnittstellen auf Datenbankebene nicht offengelegt werden, können derartige Treiber nur von den Datenbankherstellern selbst entwickelt werden. Da dies jedoch fast immer mit propietären Erweiterungen verbunden ist, ist die Datenbankunabhängigkeit leider nicht gegeben und unterstützen somit direkt die schnelleren datenbankspezifischen SQL Erweiterungen. Bei einem Datenbankwechsel sind hier ebenfalls größere Änderungen am Quelltext nötig.
Es bleibt eigentlich folgendes festzuhalten:
Die JDBC ODBC Brücke ist aus Geschwindigkeitssicht sehr schlecht ermöglicht jedoch einen Datenbankzugriff auf fast jeder Plattform. Für das Testen Ihrer Anwendung ist diese Lösung jedoch brauchbar.
Plattformeigene JDBC Treiber erreichen zwar eine gute Performance sind jedoch nicht Plattformunabhängig und daher im Zusammenspiel mit Java ein schlechte Alternative. Universelle JDBC Treiber ermöglichen Ihnen bei weittestgehender Plattform- und Datenbankunabhängigkeit eine gute Performance und sind daher erste Wahl. Direkte JDBC Treiber sind in Sachen Geschwindigkeit durch die anderen Treibertypen nicht zu schlagen. Negativ ist jedoch, dass eine relative hohe Abhängigkeit von der Datenbank die Folge ist.
Eine wirkliche Datenbankunabhängigkeit zu erreichen ist trotz JDBC leider bei weitem nicht so einfach wie sich jeder Entwickler erhofft hat. Hier ist es eine Frage Ihres guten Design die Datenbank soweit zu kapseln, dass ein Austausch dieser nicht zu enormen Quelltextänderungen führt.

Netzwerk

Java ist ein Programmiersprache für ein Netzwerk. Dies zeigt sich unter anderem an den zahlreichen Unterstützungen. Neben der Unterstützung der Soketabfragen exitstieren auch vorgefertigte APIs für RMI, Corba oder auch das Versenden von eMails. Auch im Bereich der möglichen Javaanwendungen sehen Sie, mit Applets und Servlets, bereits die Ausrichtung von Java auf Netzwerkanwendungen. Gerade in diesem Bereich ist die Plattformunabhängigkeit von Java ein wesentlicher Vorteil. Netzwerkübertragung ist unter Java ein üblicher Datenweg und nimmt auch im generellen Entwicklungsgeschäft mehr und mehr Platz ein. Technologische Ansätze bzw. Verbesserungen können im Ändern der Netzwerkstruktur z.B. von Token-Ring-Netzwerken auf Ethernet-Netzwerk oder Erhöhung der Bandbreite erfolgen. Eine Bandbreitenerhöhung ist natürlich nur sinnvoll, wenn die Banbreite auch der tatsächliche Flaschenhals ist.

Applets

Applets sind besonders im Internet oder für Datenbankabfragen weit verbreitet. Mit Einführung des Tools jar hat Sun Microsystems hier eine Möglichkeit zur Performanceverbesserung ermöglicht. Dazu wird das Applet inklusive benötigter Ressourcen (Grafiken, Musik,...) in eine jar Datei gepackt. Nachdem dies geschehen ist, müssen Sie lediglich den Aufruf in Ihrer Webseite ändern. Ein derartiger Aufruf könnte nun wie folgt aussehen:
<-- vorher:
   <applet code=”App.class” heigth=200 width=200>
      <p>Sorry hier war ein Applet!</p>
   </applet>
//-->
<applet code=”App.class” archive=”a.jar” height=200 width=200<
   <p>Sorry hier war ein Applet!</p>
</applet>

Leider hält es auch hier jeder Browserhersteller etwas anders. Während Netscape jar und zip Archive unterstützt lässt der Internet Explorer lediglich cab Archive zu.
Internet Explorer
Eigentlich handelt es sich bei dem folgenden Tipp nicht tatsächlich um eine Möglichkeit die Netzwerkperformance zu erhöhen. Der Tipp ist jedoch insbesondere in Zusammenhang mit Applets zu gebrauchen, so dass er irgendwie hier hinpasst.
Da es sich selbst in der JRE immer noch um einige Klassen handelt, wird diese normalerweise in Archiven ausgeliefert. Um die Zugriffsgeschwindigkeit zu erhöhen können Sie die dort enthaltenen Dateien in ein Verzeichnis „root://java//classes“ entpacken.
Benötigen Sie jedoch mehr Festplattenkapazität lässt sich auch der umgekehrte Weg gehen. Die Archive sind tatsächlich normalerweise nur eine Sammlung der Dateien ohne Komprimierung. Sollten Sie mehr Platz benötigen erstellen Sie die Archive mit einer beliebigen Komprimierung neu.
Sollten Sie Entwickler sein oder Applikationen starten müssen so gelten die vorgemachten Aussagen sinngemäß für die Datei „classes.zip“. Ggf. müssen Sie jedoch noch den CLASSPATH anpassen.
Inhaltsverzeichnis
all rights reserved © Bastie - Sebastian Ritter @: w³: http://www.Bastie.de
Diese Seite ist Bestandteil der Internetpräsenz unter http://www.Bastie.de


Java Cobol Software Resourcen Service Links Über mich Zum Gästebuch Forum