Slashdot Mirror


Optimizing Java?

cllajoie asks: "Every programing language has a list a mile long of optimization tips ranging from the mundane to the cryptic. I was wondering if anyone has compiled a list of these tips and tricks for Java. Some things are fairly easy, like adding final to methods so that they get inlined by the compiler, but what other deeper, darker secrets does Java have for making code faster and what are their implications on code size and memory usage? For example, if I had an accessor method is it better to use the this "pointer" in the return statement or not, or does it make any difference at all in performance? I mean lets face it, Java isn't blazingly fast to begin with so every little bit in the area helps."

2 of 29 comments (clear)

  1. K&R on optimization; tips by woggo · · Score: 3
    K&R had two simple rules for optimization:
    1. Don't do it.
    2. (for experts only) Don't do it yet.

    With those in mind, there are a number of things you can do to improve Java performance.

    • Minimize object creation at all costs. This means, don't use the "+" operator for Strings at all in production code, if possible. Remember that Strings are immutable and that every constant string in your code is another String object. It is far (hundreds of times) cheaper to use StringBuffer.append() instead. (If you don't believe me, use javap -c and check the bytecodes!)
    • Do refactor your code. Smaller methods are not only easier to read, maintain, and reuse, but they are also more hospitable to profiling.
    • You are using a profiler, right? I've used JProbe in industry, and it's output is a lot more useful than that of java -prof but it's pretty expensive to buy if you're on your own. A profiler is a must, though, because it lets you know what to optimize.
    • Avoid unnecessary synchronization. This means (if you're using Java 2), prefer HashMap to Hashtable and ArrayList to Vector. (The newer collections classes are by default not synchronized.)
    • Memoize values you use a lot. Actually, memoize any value you use more than once. javac by default won't do a lot of optimizations (like moving loop invariants outside of a loop), and some query methods are expensive. It is also a lot cheaper to access a local variable than an instance or static variable -- so any time you can cache the result of something you're using more than once, do it.
    • Don't initialize unnecessarily. This is Java, not C++, and all variables are by default initialized (to 0 for integer and float types, false for booleans, and null for reference types); initializing integers to zero will just make object creation more costly.
    • Be careful about using default constructors. If you're creating a lot of Hashtables that don't need to hold a lot of data, you'll be paying for the 1000-item default capacity if you don't specify a size.
    • Pay for things once; pay for things all at once. If you need to look up mainly-static values from a db table (for referential integrity), read them all into a map of some kind, rather than doing a SELECT for every one. Also, prefer buffered I/O whenever sensible.
    • Most of all, never resort to dirty tricks that make your code unreadable for the sake of a few more cycles. It's just not worth it.

    You an find a lot of great performance suggestions in Peter Haggar's excellent Practical Java, a sort of Strunk and White or "Effective C++ for Java".

    Good luck.


    ~wog

  2. Confessions of a former java performance bitch by Mr+T · · Score: 3
    I can speak as a former Java performance bitch, there is a lot of things you can optimize and it's also a job that you don't really want to get stuck with. This was the most miserable job I ever had and I'm honestly embarassed and ashamed of the product we made, I have since left the company and with each month that passes I'm becoming more accepting of the experience and I'm seriously thinking about writing a book (not just on java performance but on dysfunctional teams and the worst side of software engineering) The IBM "Infoprint Manager Java GUI" was truly a great example of how to not do software engineering, I'd really enjoy any comments if any of you have been unfortuante enough to have used it.

    my best advice, the most important thing you can do is think small. You don't want to build big applications in java, you want to build small cute little applications that don't do a lot on their own. This is against the trends and norms of application development these days, today everybody wants to build these huge full featured integrated apps. Not a good idea in java. I worked on a 250,000 line java application that was designed to look and act like a normal windows application, only it never will perform like a normal windows application and java doesn't have a lot of things that normal windows apps do so you spend time building them (like wizards and that ilk.) Make java apps with java.

    If you do that you will do two things, you keep the problem space small and you will keep it simple. This is critical. Some GUI things in java will just never be as fast as their native counterparts. That doesn't mean they can't perform acceptably and it doesn't mean java doesn't do other things very quickly. Paint()/WM_PAINT is just never going to be as fast in java as it is in C++. If you're building a visual component and blit-speed is the hangup then you have to reduce the number of blits or figure out a way to just deal with it. Your managment need to understand that there are classes of problems that java won't do as quickly and you have to be able to determine if your problem is one of those. Our app was so damn complex that it was difficult to pin point a single area of poor performance, let alone formulate improvments or calculate the benefit from spending the time on it. It was a classical build up failure, you could point optimizeit at it and the data told us that everything is slow, or rather everything is pretty fast but not lightening and so there wasn't a clear place to optimize, the little stuff adds up. Mix in a liberal dose of kingdom building and code protectionism and nobody on the team wanted to rework "their code" because they wanted to see what could be gained first but the analysis showed that everyone needed to do it. Worse, optimizing one piece of code didn't yield an exciting double digit performance increase, it was a tiny 6% increase and selling that to the other team members is a hard sell. "you mean I have to rewrite all my code and it's only going to be 5-6% faster? screw that!" If the app was simple this would have been a problem that is tractable.

    Listeners leak. Creating dialogs with buttons and just letting the listeners go will cause memory leaks. Listeners register with AWT and need to be explicitly unregistered. Listeners point back to your buttons which often point to dialogs and even data. This is particularly problematic when you have a view/model type abstraction. We had literally hundreds of dialogs and buttons and essentially no GUI elements were ever garbage collected. This was an extremely non-trivial problem to fix and we didn't do it before I quit the company (IBM.) We had an object hierarchy in place where listeners were completely ignored during the design process. Many dialogs had multiple exit points. There just wasn't a clean way to fix the problem without making thousands of code changes. More alarming, certain members of the team refused to even acknowledge that there was a problem: "java doesn't have pointers...", "java has garbage collection.. it's impossible..." Once one person on the team was convinced and they started to grasp the gravity of it, they asked to get off the project and then they started trying to down play the performance significance because it was too big a problem to fix in our time frame. Here is how it kills you: GUI elements are built in to a tree in java (at least swing elements are.) That tree encompasses your entire visible/instantiated GUI, you do an update and that tree is traversed and each element is told to update itself. It's a very clean and simple model. The problem is when the GUI starts to contain hundreds and thousands of elements those updates start to take more and more time. Click, traverse tree, update update update...., see response. Add garbage collection and get close to filling the memory on the system and you've got a real performance problem, every time an event happens you will end up having to swap to update the tree and GCs will do it too. This is almost a non-problem if you keep it small, you may leak like a maniac but you have to leak megabytes and megabytes for it to really matter. We did so I know this. By the time you notice it, your app is way to big and unwieldly for this to be easy to fix.

    Be smart with threads, reflection, and all the other goodies. Some of the language features java gives you are a dream to use, they also extract a price. Creating dialogs (or rather "notebooks") with hundreds of elements via reflection can be a hazard. Creating objects is expensive, do it as seldom as possible, write a pooling system if you have to. Be smart about data flow, with a view/model GUI you're going to be communicating between the view and model a lot, that adds up. Threads are addictive, they can also make the analysis of performance very difficult to understand, there is also an innate cost to creating and destroying them (provided that your don't have so many f-ing leaks that you can actually destroy a thread...) I never found a way to measure the time it takes for the JVM to traverse the class tree hierarchy, I have to assume that doing it repeatedly with a larger tree will cause a noticable difference.

    There should be something said about JNI but I'm not sure what. Don't use it unless you benefit from it. Don't expect it to be a silver bullet. I think that with the JVMs of last year and our project, we used JNI so we could use a legacy communication library, JNI caused more problems than good. No easy ways to measure it's performance.

    --
    This is my signature. There are many signatures like it but this one is mine..