Erfahrungsbericht Java Specialist Master Course

5 Kommentare

Letzte Woche durfte ich zur Weiterentwicklung meiner Java Fertigkeiten den Java Specialists Master course von Heinz Kabutz besuchen. Java Champion Heinz, ist ein hervorragender Trainer, der es geschafft hat Anekdoten, harte Fakten und tiefe Javakenntnisse zusammen mit herausfordernden Übungen zu einem großartigem Kurs zusammenzustellen. Der Umfang betraf das gesamte Spektrum von Java, jedoch lag der Fokus auf den kleinen Gemeinheiten die man entweder nicht kennt oder nicht nutzt. Auszüge des Kursmaterials veröffentlichte er bereits in seinem Newsletter, welcher eine große Leserschaft weltweit besitzt.

Im folgenden möchte ich meine Eindrücke der 4 Tage schildern…

Tag 1

Direkt mit einem komplexen Thema ging es Dienstag früh los: Thread, wie funktionieren sie und was gibt es zu beachten. Wir haben eine ThreadGroup erstellt und zu einem eigenem Thread pool ausgebaut. ThreadGroup ist nun leider nicht die allerschönste Klasse. Heinz verglich sie mit einer Art Kinderzeichnung aus den frühen Java Jahren. besonders einfach zu benutzen fand ich die java.util.concurrent Locks, welchen ich bisher weniger Beachtung schenkte. Endlich sind die Zeiten von synchronized() vorbei. Noch vor dem Mittagessen erläuterte Heinz uns seine Gesetze über Nebenläufigkeit, welche er bereits in verkürzter Form auf unserem meet the experts – performance event präsentierte. Dabei stolperten wir über folgenden code:

private boolean running = true;
public void dojob() {
  while(running) {
    // do something useful
 }
}
public void shutdown() {
  running = false;
}

Führt man diesen auf einer Server VM aus (java -server), so kann es passieren daß der Code sich nicht mehr beendet, da die HotSpot engine den Code optimiert, da running scheinbar immer true ist. Um dies zu vermeiden muss es volatile gemacht werden. Da ich nach Debugging fragte versuchten wir in der Schleife anzuhalten und sieh da, der Debugger zeigte und: running = false. Dies hielt aber den laufenden Code nicht davon ab weiterzulaufen, da der Debugger zwar den korrekten Wert sieht, der laufende Code aber nicht. Doch es wird noch interessanter:

public void doJob() {
  boolean myRunning = running;
  while(running){
    // do something useful
    myRunning = running;
  }
}

Nun zeigt uns der Debugger dies:

running = false; myrunning = true;

trotzdem läuft der code weiter. Jedoch nur so lange bis wir mittels F7 die Zeile ausführen ließen. Diese Art von Problemen kann ein Alptraum werden, deshalb ist es wichtig zu wissen was man korrekterweise tun muss.

Auch wissenswert war es zu erfahren, daß

if (Thread.interrupted()) {
  throw new InterruptedException()
}

der erste Code in jeder Methode sein sollte welche eine InterruptedException deklariert.

Wir haben außerdem den CompletionService kennengelernt, welches nach einem interessanten Interface für die Bearbeitung von asynchronen Aufgaben aussieht. Wer braucht da noch closures? 🙂

Tag 2

Wir begannen Tag 2 mit Java new (yet another new?) IO, welches recht viele Features mitbringt, aber irgendwie doch selten verwendet wird. Möglicherweise liegt das an der teilweise sperrigen Benutzung der Interfaces. Man sollte doch vielleicht einfache Beispiele probieren bevor man sich an einem asynchronen nichtblockierendem Server versucht (was man aber durchaus nach Besuch des Kurses kann 🙂 ).

FileChannel fc = new RandomAccessFile("test.txt", "r").getChannel();
MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());

Die zweite Tageshälfte verbrachten wir in den Untiefen der Java Speicherverwaltung. Natürlich ist die Praxistauglichkeit des Themas reduziert, doch das Verständnis ist essentiell um Probleme zu verstehen und zu lösen. Wir sahen uns caching und pooling an und betrachteten warum dies Objekte leaken oder herumlungern lassen kann. Das Thema erinnerte mich an einen alten post über tag pooling in Servlet Containern. Ich habe nie wirklich verstanden warum Tags gepoolt werden müssen, und wieso Tags keine erkennbaren Lifecyclemethoden besitzen anhand derer man das Pooling erahnen könnte. Als Tools setzten wir jVisualVm und HPjMeter ein, um den Garbage Collector bei seiner Arbeit zu beobachten.

Tag 3

Ich lernte einige interessante Verhaltensweisen von bislang von mir nicht benutzten Collection Klassen, wie zum Beispiel PriorityQueue kennen, sowie einige Besonderheiten von Classloading. Heinz schaffte es java.lang.reflect.Proxies hervorragend zu erklären, so daß die Anwendung in der Übung leicht viel. Genaugenomen ist die beste Erklärung Teil der JavaDoc, doch man muss erst verstehen wie man diese zu lesen hat:

Foo f = (Foo) Proxy.newProxyInstance(
		Foo.class.getClassLoader(), new Class[] { Foo.class },
		new InvocationHandler() {
		  public Object invoke(Object foo, Method method, Object[] arguments) throws Throwable {
		    return method.invoke(foo, arguments);
		  }
		});

Nachmittags diskutierten wir über Exceptions, und ich hatte Gelegenheit mir eine begründete Meinung über die Verwendung von checked vs. unchecked Exceptions zu bilden. Ich werde in Zukunft unchecked Exceptions für Entwickler- bzw. Programmierfehler verwenden. Diese zu fangen ist nicht nötig, die Anwendung mag sogar Abstürzen, aber der Fehler muss behoben werden. Jedoch alle umgebungs-, oder installationsrelevanten Besonderheiten werde ich mit checked Exceptions behandeln. Idealerweise bieten diese brauchbare Informationen damit der fangende Code eine brauchbare Fehlerbehandlung durchführen kann. Eine weitere wichtige Erkenntnis: Einfach Exceptions weiter werfen! Ich weiß gar nicht genau warum ich das bisher kaum tat. Wahrscheinlich wollte ich meine Methodensignaturen einfach sauber halten. Dabei ist weiter werfen oft die beste Alternative. Insbesondere die InterruptedException, welche sowieso nur sinnvoll im Threadcode behandelt werden kann (mit Aufruf von interrupted() und dem Verlassen der Schleife).

Tag 4

Der letzte Kurstag begann mit einer echt harten Nuss. Es galt die Performance einer Methode drastisch zu verbessern. Doch Heinz ließ uns nicht an die Optimierung, bevor wir nicht alle Zahlen in verschiedenen Szenarien hatten. Zuvor galt es sogar noch zu beweisen, daß unser Testcode keinerlei Auswirkungen auf die Performance des zu testenden Codes hatte. Ich fand dies besonders lehrreich, da ich durchaus manchmal vergessen habe das Problem zu beweisen, bevor ich mit der Lösung begann. Als Randnotiz betrachteten wir die verschiedenen Modi der JVM und fanden heraus wie langsam java -Xint ist. Nachdem ich endlich den Code auf 10% seiner Laufzeit kürzen durfte gingen wir über zu den letzten Themen des Kurses. So behandelten wir kurz Datum und Zeit, wobei ich doch lieber jodatime oder icu4j nutze und versuche java.util.Date zu vermeiden. Zuletzt sahen wir uns noch Logging und einige Kniffe zur Optimierung von Loggingausgaben an. Die wichtigste Botschaft bei Logging ist Code Wächter zu verwenden. Zwar war mir die Technik bekannt, der Name dafür jedoch neu:

if (log.isDebugEnabled()){
  log.debug(complexObject.toString() + expensive.toString());
}

Wrap-Up

Ich kann den Kurs bedenkenlos weiterempfehlen. 4 randvoll mit Informationen und Übungen gefüllte Tage sind derartig gestaltet, daß sie auch für erfahrene Entwickler herausfordernd sind. Als Teilnehmer sollte man unbedingt einige Zeit mit Java gearbeitet haben um die Nuancen und Besonderheiten zu erkennen. Anfänger könnten zwar folgen, verpassen aber interessante Aspekte, da das Tempo recht hoch ist. Ferner empfehle ich den Kurs in Deutsch zu besuchen, da Heinz unterhaltsamen Akzent hat 🙂

Fabian Lange ist Lead Agent Engineer bei Instana und bei der codecentric als Performance Geek bekannt. Er baut leidenschaftlich gerne schnelle Software und hilft anderen dabei, das Gleiche zu tun.
Er kennt die Java Virtual Machine bis in die letzte Ecke und beherrscht verschiedenste Tools, die JVM, den JIT oder den GC zu verstehen.
Er ist beliebter Vortragender auf zahlreichen Konferenzen und wurde unter anderem mit dem JavaOne Rockstar Award ausgezeichnet.

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • Thomas Ferris Nicolaisen

    I’m not sure that I like the part about log code guards. Is this really *the* most important lesson to learn about logging and performance? IMHO, isDebugEnabled is noise which extremely rarely is justified by the saving the performance hit of .toString.

    What kind of performance benefits did you achieve, and how expensive was this toString?

    Also the checked exceptions.. Saying „everything which is related to the Environment the app runs ins should work with checked exceptions“ is too big of a generalization for me. What about data access exceptions and IOExceptions? In many cases you can’t deal with these, so having them checked is more annoying than useful.

    I’m afraid that I meet those two pieces of belief quite often, although most of the really smart developers I know have long since abandoned them with good reasons. I think it’s a bit scary that Kabutz is still spreading these old tricks.

    Mind, that the rest of the course is probably great, it’s just these two points that bother me.

  • Fabian Lange

    Hi Thomas,
    code guards are important. the if is less expensive than String object creation. This is not caught by Escape Analysis (-X:+DoescapeAnalysis) because it creates objects in the String pool. It also creates a method call that needs to copy parameters onto the stack. You can profile it. It is almost always slower than the ugly if.
    You can speed it even more up by using a public static final boolean DEBUG = false;
    this will remove the if + debug line during compilation.

    Regarding Exceptions: Yes they are annoying. But wouldn’t it be better to be able to do a sensible retry? Very often you can’t do a sensible retry, so you end with aborting, or worse, ignoring.

    If being smart means you would have a catch for RuntimeException, I disagree. you should not catch RuntimeExceptions.

    Also note that the Exception decision was my very own. Heinz has a similar but not one that I could repeat in detail. However the code guard is the right thing to use if you care about performance of your logging statements.

  • Thomas Ferris Nicolaisen

    Hi Fabian, thanks for your answer.

    To be clear: No, I don’t really care about the performance of the log statements. I think there are so many other places in code I would work on performance (double for-loops with quadratic run time for instance).

    If it was an embedded performance critical application, I would perhaps see the need for it, but as long as you’re in a web application, or anything that touches disk or is distributed, you will not get a practical benefit from this. The already code-noisy log.debug statements will now be 3 lines each instead of just one.

    Regarding exceptions, we may be on separate sides of the checked/unchecked chasm/flamewar subject.

    I know it’s hard to convince people of this, but it’s a bit like the log statements: Good ideas in principle, but in practice, average developers end up surrounding *all* log statements with isDebugEnabled (even when they run with debug enabled in production), and making all exceptions checked because „it’s safer“.

    Keep in mind that checked exceptions is something of an experiment that the developers of Java introduced to try it out. They don’t exist in any other programming language AFAIK.

    The long term effect of having them in the language can be widely discussed, but my opinion (and many others) is that we would be better off without them, as they have been overused, by a myriad of developers as well as the JDK developers. As food for thought, the C# originators chose to leave checked exceptions out of .Net..

    The best thing I can do is to refer to this list of links discussing the subject: http://www.java.no/forum/posts/list/10946.page (Norwegian page but English links).

    I hope you can take the time to look at some of those links, maybe you’ll change your mind about it 🙂

  • Mirko

    9. März 2010 von Mirko

    Hi Thomas,

    I’ve seen missing isDebugEnabled(…) Statements as the reason for bad performance in many performance tuning assignments. Especially if one of these three points is true:

    – The methods with the debug-Statement are called very often (I’ve seen this with millions of call per requests)

    – The String that should be logged is „complex“ – means that it is concatinated AND dynamic, e.g. logs a lot of parameters/attributes.

    – The object that is logged has a „expensive“ toString() method, which is implicitly called – in many cases I’ve profiled this and the developers didn’t even know that the toString() was so expensive, as they used others components/frameworks.

    So yes, isDebugEnabled() is ugly, but in many cases necessary (in others maybe not) – as it can be performance critical I would always advise to set up coding guidelines that include a mandatory isDebugEnabled().

    With regard to the exceptions: There are so many discussions about it that there is no „good“ or „bad“ in my opinion. The only thing I would suggest is to have a common usage guideline for a whole project.

  • Nikos

    Hi Fabian, very nice post! You are lucky to attend a course of Mr. Kabutz. Thanks for sharing these interesting points.

    Kind Regards,
    Nikos

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.