The Source for Java Technology Collaboration
User: Password:



Tom Ball's Blog

April 2005 Archives


Optimize a Swing App by Slowing It Down

Posted by tball on April 11, 2005 at 04:54 PM | Permalink | Comments (4)

Swing is slow, right? That seems to be a common perception which fast Swing apps like LimeWire seem unable to shake. Even the Jackpot GUI app is sluggish these days, and we all know it cannot be that my code is at fault, right?

I think the real problem is that yours and my computers are too fast, or at least too fast relative to our customers' systems. Because software developers and quality testers tend to have very fast systems so they can multi-task effectively, an unfortunate side-effect is that they tend to run their applications faster than their customers do. As a result, it's easy to overlook inefficient and redundant GUI operations as long as the result looks and feels okay on a fast system. Slow your system down enough, however, and these problems leap out at you, demanding attention.

One easy way to slow down a Swing application is to turn on DebugGraphics functionality. Although DebugGraphics was designed to display potential problems with display painting and layout, it has a nice side-effect of pausing after each paint operation (otherwise you wouldn't be able to see it work). Here is an example of how one might use this:

    private static final boolean debugGraphics = true;
    ...
    if (debugGraphics) {
        DebugGraphics.setFlashTime(100);                 // 100 ms. pause
        DebugGraphics.setFlashCount(1);                  // only pause once per paint
        DebugGraphics.setFlashColor(new Color(0,0,0,0)); // no color

        // turn off double-buffering, or this gets optimized away!
        RepaintManager.currentManager(myComponent).setDoubleBufferingEnabled(false);
        myComponent.setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION);
    }

The first time you run this code, do so in the debugger. If it appears to hang, suspend the app and see where it is stuck. When I first ran it on my app, I had turned DebugGraphics on for my whole component tree -- DebugGraphics was pausing after each bump was drawn on my JSplitPane slider! Not very useful...

Once I only turned debugging on for my main panes, it became painfully obvious that some operations of my apps GUI operations were redundant. My app has a JTable which is supposed to select a JTree node when a row is clicked, but selection was happening multiple times. Setting a breakpoint on my ListSelectionListener showed I needed to first check ListSelectionEvent.getValueIsAdjusting() and only handle the event if its false; failing to do so was causing a new list selection event to be fired for each entry in the tree path for that node (maybe Swing wasn't the problem after all!). These multiple selection events were not that noticeable on my fast system, but after fixing this and other similar problems, the GUI is much perkier.

I am going to leave this code enabled in my app for a few more days. The slow display will irritate me a lot, and so I'll just have to make it faster some other way. Wish me luck!

Exceptional Debugging

Posted by tball on April 04, 2005 at 03:36 PM | Permalink | Comments (9)

Exceptions (and their stacktraces) are one of the features that first attracted me to Java back in early 1995; C++ obviously had exceptions, but I never worked with any C++ projects that used them as effectively as the Java core classes did. I find well-designed exceptions to be a significant productivity booster.

My first project on the JDK team was developing the debugger API, and I think it was the first debugger where you could set a breakpoint that tripped whenever a specified exception type was created (please correct me if you know of earlier systems). The justification for exception breakpoints was simple: exceptions (should) indicate abnormal control flow in a program, and since we spend most of our time in debuggers figuring out why our programs are acting abnormally, there should be an easy way to halt whenever the program is indicating something worth investigating.

But few engineers I've met use this feature, and it puzzles me a little. Most recently, I filed a NullPointerException problem which didn't log a stacktrace, and the assigned engineer first added logging code so he could get a stacktrace to see where the problem was, which he then spent a lot of time reading various source files to determine what could have caused it. If he had instead set an NullPointerException breakpoint, the debugger would have stopped at the offending statement with all of its local variable and stack information immediately available. This is much, much faster than running until a stacktrace prints, editing the code to add debug statements if the cause isn't obvious, and looping until the problem is figured out. Isn't impatience supposed to be one of the attributes of a good programmer?
Exception breakpoints can be useful even when you aren't debugging exception failures, too. In NetBeans, I always have breakpoints set for NullPointerException and AssertionError (running with the -ea flag), to catch these conditions as soon as they rear their ugly heads.

I have heard complaints that exception breakpoints are "too noisy" because they can trip in places you don't expect. I think this indicates a couple of potential design flaws:
  • Overly general exceptions: although you don't need a separate type for all possible exception cases, it is often very useful to do more than "throw new InternalError()" when a problem arises. The java.io package derives FileNotFoundException and EOFException from IOException, since for many apps these are much more common recoverable error conditions than, say, a SyncFailedException. A custom exception is a great documentation aid if you have a public API, and exception classes are simple to create and maintain.

  • Using exceptions for flow control: as Josh Bloch argued in Effective Java, "Use exceptions only for exceptional conditions" (item 39). In the Solaris JDK a few years back, for example, system font files were discovered by trying to open all possible font names and catching all the FileNotFoundExceptions. This was much slower and more memory intensive than using File.exists(), due to all the exception object creation and collection. If your program is querying a class by poking it and testing for exceptions, add a method to that class to get the information directly.
Another complaint is that exception breakpoints catch exceptions in OPC ("other people's code", the source of all developers' woes :-). For example, Swing uses NullPointerException for flow-control in a couple of places, so I filed bugs when they tripped (should be fixed in Mustang) and now just hit continue. I strongly recommend filing such bugs as you find them (along with a recommended patch), so as to eliminate these nits and improve the Java platform as a whole. After all, the more successful it is, the more successful we Java developers will be.



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds