Archive for August 2005

Ad-Hoc Exception Handling

Gerne nutze ich während des Entwicklungs die Fähigkeit moderner Entwicklungsumgebungen automatisch try-catch Blöcke zu erzeugen. Man möchte schließlich die Funktionalität fertig kriegen.

Normalerweise kommt dabei sowas raus:

try{
  doSomthingInteresting();
} catch (Exception e) {
  //todo: error handling
   e.printStackTrace();
}

Der Nachteil ist offensichtlich. Der Tag an dem man alle Todos abarbeitet kommt nie. Irgendwann tritt der Fehler auf. Die Exception wird gefangen, die Anwendung läuft weiter und der Fehler bringt die das Programm im weiteren Verlauf ins Straucheln. Die Standardausgabe ist womöglich nicht verfügbar, außerdem könnten mittlerweile weitere Exceptions aufgetaucht sein – der Stoff aus dem ruinierte Nächte gemacht werden.

Ich schlage stattdessen folgendes Schema vor:

try{
  doSomthingInteresting();
} catch (Exception e) {
  throw new RuntimeException(e);
}

Die Exception wird so nicht verschluckt, aber zunächst muss sie nicht behandelt werden. Dieses Template kann man auch den gängigen IDEs beibringen.

Um die Fehlerdiagnose weiter zu vereinfachen bietet es sich an der RuntimeException noch eine gute Message mitzugeben:

try{
  doSomthingInteresting();
} catch (Exception e) {
  throw new RuntimeException("Could not do InterestingStuff",e);
}

Eine Verfeinerung des Ansatzes besteht darin bestimmte eigene Runtime Exceptions zu werfen und diese außen im Eventloop (oder Request) zu fangen und entsprechend zu reagieren (Rollback, Fehlermeldung).

Java Performance

Über die Frage welche Operation wieviel Zeit benötigt wird viel und wild spekuliert.
Es werden zahlreiche trickreiche Optimierungen gebastelt – oft von geringem Nutzen und größter Unübersichtlichkeit.

Um etwas Licht ins Dunkel zu bringen habe ich unlängst auf meinem P4- 3,2GHz System unter WinXP und JDK 1.4.2 ein paar Messungen durchgeführt. Ein Takt dauert auf dieser Maschine 0.31ns

h3. Methodenaufruf

| Aufruf eines Setters für ein non-primitive Attribut|>. 1,1 ns|
| Downcast (dynamische Typprüfung)|>. 3 ns|
| Aufruf über Reflection|>. 400 ns|
| Method Lookup|>. 3200 ns|
| HashMap.put mit 4-16 Zeichen Schlüssel und 4-12 verschiedenen Schlüsseln|>. 80 ns|
| HashMap.get mit existierendem Schlüssel|>. 53 ns|
| HashTable.putmit 4-16 Zeichen Schlüssel und 4-12 verschiedenen Schlüsseln|>. 190 ns|

h3. Dateien Schreiben, konstanter Anteil
| Testmachine (Win XP, NTFS) |>. 2000000 ns|
|PIII Celeron 1GHz and (Linux, ReiserFS): |>. 500000 ns|

h3. Objekterzeugung
| Erzeugung einer Stringinstanz aus einem Literal|>. 85ns|

Erzeungung eines Objects mit n non-primitive fields
| n | t |
|>. 2|>. 56 ns|
|>. 4 |>. 59 ns|
|>. 8 |>. 68 ns|
|>. 16|>. 86 ns|

h3. Moral

Bei Optimierungen ist zu beachten, dass zwischen den verschiedenen Operationen teilweise sogar mehrere _dezimale_ Größenordnungen liegen.
Es lohnt sich also beispielsweise kaum einen Comparator über mehrere Sortiervorgänge zu cachen.
Außerdem sollte man im Auge behalten, wie oft eine Operation ausgeführt wird und welche OPeration die Laufzeit dominiert.