Want to read Slashdot from your mobile device? Point it at m.slashdot.org and keep reading!

 



Forgot your password?
typodupeerror
×
Programming IT Technology

Optimizing Java? 29

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."
This discussion has been archived. No new comments can be posted.

Optimizing Java?

Comments Filter:
  • I have been doing a lot of biostatistics work lately, and have found a combination of the default hprof and the commercial NuMega DevPartner for Java a good pair. Our code usually ran for about an hour and a half per run, so dropping the time by 10% saved us almost 10 minutes. The others who have said "do not optimize early" are quite correct - it is always worth a quick profile once the code works to see if there are any easy points, but once those are done, it is a hard call. Is it is worth a few days rewriting a method that takes 5% of the time to drop it to 4.95%? Given what a coder costs, likely not. On the other hand, it is probably worth it to drop a 60% down to 5%, since then you start to see major performance improvements. As always, algorithmic improvements are probably better anyway. Looking for ways to precalculate things is handy, as it getting calculations out of a loop, but even that might well be done by your jitc. This does not mean you should code foolishly and hope the jitc gets it. For example, StringBuffers are faster than concatenating strings in all cases, so use them as soon as you have enough plus signs to justify it. I do not usually use them in exceptions, but I do in any main line code with more than one plus sign. Joshua Bloch, co-author of the Java2 collections classes, has a new book entitled "Effective Java" that is on my must buy list. He may well have some good usage tips. Scott
  • Default size for Java Hashtable is 11 and grows by (size+1) every time it needs resizing. Surely you can afford to allocate 11 pointers == 44 bytes when only your virtual machine takes ~20 megs.
  • Apologies for the complete lack of formatting in my last post. I hit submit rather than preview.

    Mea Culpa

    Scott

  • If speed is that much of an issue, use the GCJ compiler (or TowerJ for that matter) and turn it into a binary. Little "tricks" like that usually end up making the code hard to maintain and can cause subtle errors to creep in. Also, your choice of VM can have a lot to do with the performance of the code ...
  • At a guess I'd say that what works for one langage will work for another, get some books on algorithms and programing in general.

    No first hand experience in that sort of thing, I was kicked out of university, went in at the deep end, and now just write code that is quick dirty and meets (plus or minus the odd month, year, decade) deadlines.

    Probably your best answer is to kick 7 shades out of your marketing & management, make them your bitches and you'll have the freedom to code good stuff.

  • by Anonymous Coward
    Check out http://www.compapp.dcu.ie/~jwal sh.ca4/funcspec.html [compapp.dcu.ie].

    The functional spec. outlines the areas where Java sucks most and the program fixes them up automatically.
  • I just discovered the -prof option. Just use java -prof [whatever], and you will be treated to a java.prof file that contains quite detailed information about CPU usage, down to the function call.

    I discovered that operations on Strings are very very time-consuming.

    I'd estimate a 2x speedup after optimizing three functions, thanks to -prof.
  • This page is no longer updated but at one time it was the only page on the internet about optimizing java:

    http://www.cs.cmu.edu/~jch/java/ [cmu.edu]

    Leknor
    http://Leknor.com [leknor.com]
    Leknor
    http://Leknor.com [leknor.com]

  • The AC said:
    How the hell do stories with no comments find their way into the search list, but never reach the front page?
    Many stories never make it to the front page, they sit in the Slashbox appropriate to their section. There are a lot of Ask Slashdot's which never make it to the front page. Try fiddling with the settings, add the Ask Slashdot Slashbox to your homepage (remember, check everything in bold if you want to keep the default settings) and watch the box for questions/articles which don't appear on the front page. I have all of my favorite sections on the front page, and I spend about the same amount of time on those articles as I do on the ones from the front page.

    Louis Wu

    "Where do you want to go ...

  • GCJ often produces code that runs slower than Java code inside a decent JIT. I have rarely seen instances where GCJ is useful for anything but server-side code, and its limits with respect to dynamic class loading often make its performance worse than that of a good JIT.

    The choice of the VM can have a tremendous impact on the speed of code. For instance, Sun's HotSpot VM still sucks for client side code. For server-side code, HotSpot (and its garbage collector) can improve apparent performance considerably.
  • This is a slightly old, but good article: http://www.javaworld.com/javaworld/jw-04-1997/jw-0 4-optimize_p.html [javaworld.com]
  • by barracg8 ( 61682 )
    Trolling for Larry Wall are we?
  • Optimizeit [optimizeit.com] is a powerful profiling solution for Java developers looking to rapidly track down and fix performance issues such as memory leaks and performance bottlenecks in any Java programs. Optimizeit is essential for the detection of performance issues in application server environments. Optimizeit provides instantly integration with most popular application servers, its overhead limiting features ensure scalability and new offline profiling coming soon in 4.0 allows testing of applications in production environments
    The Queue Principle
  • This one's not so much for speed, but for cutting down size of compiled code - extremely useful when working with devices such as the TINI [ibutton.com].
    Tim Beauchamp posted this to the TINI mailing list in the last couple of days.
    >Writing debug routines that writes to stdout or some mechanism are

    >just a fact of life. But the trouble is, they add to size and
    >complexity of the compiled code.
    >
    >They can be turned off with conditionals like this
    >
    > boolean debugging_on = true;
    >
    > if(debugging_on)
    > {
    > System.out.println("I am in here");
    > }
    >...

    >Create a debug class with a static final boolean variable called
    >DEBUGGING_ON and set it to true
    >
    >public class debug
    >{
    > public static final boolean DEBUGGING_ON = true;
    >}
    >
    >Now, wherever you want debug code, use that in a conditional
    >e.g.
    >
    >if(debug.DEBUGGING_ON)
    >{
    > System.out.println("I am in here");
    >}
    >
    >The Java compiler can resolve this at compile time and will toss out
    >the entire thing when you set the flag to false.
    >
    >You can verify by building with and without it turned on and then tear
    >apart the classes with javap to see that one has the code in there and one
    >doesn't.

    Sorry about the formatting, I just cut and paste it from an email in my inbox

  • by Matts ( 1628 )
    Install Perl, be happy. :-)
  • Well, as Java is just a variant on C in many ways, sturcturing your program in such a way that it would benefit a C program (pyramided functions) seems to help. Also, make sure your development environment is set to the most accurate settings possible (this is similar to compiling using -06 w/ gcc for C).
  • Actually, you can explicitly "suggest" that the JVM collect garbage with the System.gc() method; however, I believe that the class library spec does not require the garbage collector to heed your suggestion. :-) In any case, calling System.gc() every now and then will speed you up a little if you're creating a lot of short-lived objects and if you're in a memory-constrained environment.


    ~wog
  • In 1.2.2, the default size is 101, actually. (I'd post the source, but that would be violating the "license agreement" contained in the header of Hashtable.java :-)) If you're converting from a Map (i.e. new Hashtable(new Map(...));), the default size is 11 or twice the size of the Map. Furthermore, it grows by ((size * 2) + 1). (It would be pretty slow to grow if it grew by ONE for each insert -- even Sun isn't writing code that suboptimally.)

    So I erred; mea culpa. I still maintain that it's an issue. There are a lot of applications where it is useful to create a lot of small hashtables as part of larger data structures (parsing certain kinds of languages in particular benefits from this), and if you have to allocate 101 pointers each time you make a mapping that will only use, say, 6, your performance will suffer because all that unnecessary allocation will make the GC hyperactive.


    ~wog

  • by woggo ( 11781 ) on Saturday August 19, 2000 @08:09PM (#842627) Journal
    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 [refactoring.com] 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 [klgroup.com] 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 [amazon.com], a sort of Strunk and White or "Effective C++ for Java".

    Good luck.


    ~wog

  • I was talking about the default constructor for Hashtable in JDK1.3 : A call to new Hashtable() will just call new Hashtable(11, 0.75) IIRC. Also, you got me wrong about the growing of size. I said it grows *BY* (size+1). This means that if the old size was sz, the new one will be sz+(sz+1)==2*sz+1.
    So we both meant the same thing.
    • With Jikes, you have a number of options, including obfuscation (which is just an algorithm to reduce code size -- smaller class & var names are used).
    As I understand it, the obfuscation is only for protection of source code. Using the line number & local variable name tables, it is relatively easy to decompile code with a very high level of accuracy.

    Obfuscation has relatively little impact on program size, and no impact on runtime speed. With the use of the constant pool, there is only one copy of any class name, variable name, etc. I guess the code size could have an impact when you are downloading applets. But then you should be putting stuff in a jar anyway. At runtime the longer names in the code have no impact. Even in the earliest interpreting VM from Sun, the first time a method is called / field is accessed, the code modifies itself to replace the call/access instruction with a fast version. Once you take into account the 90/10 rule....

    I could be wrong.
    It has been known to happen occasionally :-)

    • IDE's like JBuilder may actually have their use, if you just want to quickly make GUI's and concentrate on code internals. It deals with most of the Swing/AWT for you. Available on Linux. Automates some things for bad codewriters you might work with, to cut down on bugs.
    I don't use IDEs - so the following is pretty uninformed. But the slowest and most convualuted piece of code that I have ever seen came from a Java IDE. (I think VisualAge was too blame.) It actually used
    • Class.forName("...").newInstance(...);
    to create instances of other classes also produced by the IDE - it was as if the programmers didn't know the 'new' keyword existed. Urrgh.
  • by Mr T ( 21709 ) on Tuesday August 22, 2000 @08:13AM (#842630) Homepage
    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.

    • At a guess I'd say that what works for one langage will work for another, get some books on algorithms and programing in general.
    Yes, this is true, general good programming practice, and intellegent use of algorithms is important.

    But there are optomizations specific to the Java language, that people have not yet mentioned, and that everyone should know.

    1. The use of the keyword 'final' has been mentioned above. You can also get the same effect from 'private' functions. But do not damage your libraries long term usefulness in exchange for a short term speed kick.
    2. One of the (old) articles linked off another post suggests that having your java code call native code will be faster. This was true three years ago, when we had interpteting VMs. In Java 1.3, Sun has replaced a load of native math stuff in the standard libraries with java code. Doing this has increased speed of these functions by a factor of three or four. In most VMs, calling native code is very slow, and JITs are very fast.
    3. Strings are immutable. The fact that the java language overloads the + and += operators can blind people new to the language about the importance of this. This is actually implemented using the class StringBuffer - e.g.
      • class ex1 {

      • int the_int;

        public String toString() {
        return "The value is " + the_int;
        }
        }
      Compiles to exactly the same code as:
      • class ex2 {

      • int the_int;

        public String toString() {
        return ((new StringBuffer("The value is ")).append(the_int)).toString();
        }
        }
      Obviously, in this situation, the first version is probably better. (The code is clearer). But you should never use the + operator to add Strings if you are doing multiple, seperate, adds. E.g.
      • class ex3 {

      • int the_int;

        public String silly() {
        String a = "A";
        String b = "B";
        String c = "C";
        String ab = a+b;
        return ab+c;
        }
        }
      Lots of nasty, wasteful Object creation.
    4. If you are writing applets, use .jar files. The reason: if your applet has to downloaded a set of multiple seperate .class files (and probably other resources, such as .gifs), you must open seperate http request for each one. This is a lot slower than doing it all in one go.
    Hope this helps.

    Oh, to address cllajoie's question about using the 'this' pointer: this produces exactly the same code.

    • Probably your best answer is to kick 7 shades out of your marketing & management, make them your bitches and you'll have the freedom to code good stuff.
    I doubt that this would work...
    But nevertheless, I am certain that a lot more research should be done in this field.
  • 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.

    You know, if there was ever a place I thought that Java needed a "delete" keyword and destructors, this is it. Just think how much easier if you could "delete" a dialog when you were finished with it, and the dtor for the dialog could take care of removing all those listeners? This is one of the few times I wish I was still doing C++ instead of Java.

    --
    A "freaking free-loading Canadian" stealing jobs from good honest hard working Americans since 1997.
  • This, of course, is why I stated in my earlier post, "you can tell them to garbage collect on demand".

    My point is that you have little control over when the garbage collector interrupts your program and garbage collects. As a result, you will want to keep this to a minimum. To keep it at a minimum, don't do things that require garbage collection (spuriously allocating objects).
  • Start by using a decent compiler, like jikes from IBM (GPLed). javac does not much for code optimization. Then use a VM with a decent JIT. Then learn java bytecode, so you can answer your questions by decompiling byte code. Then subscribe to the jdc, from Javasoft.
  • I have done java development for several years and this is the first I have ever heard of this. Is this a documented feature? It does not appear in java -help menu.

    I tried the -prof flag on my linux computer and I immediatly got a core dump. I tried it on my windows computer and it immediatly performed and illegal operation and had to be shut down. I am of course running recent versions of java from Sun. Methinks there are a few bugs and this is no longer supported or not yet supported.

  • This poster is right on. I've been developing Java libraries for a living for a number of years (ok, only 3+ years). These recommendations summarize things wonderfully. I couldn't have said it better myself.

    One thing I would like to emphasize. Try to keep memory allocation to a minimum. Besides the obvious performance hit for allocating the memory and initializing the variables, you also run into issues with garbage collection. You really don't have too much control over garbage collection, either. I've seen one Java program in particular that would take seemingly random 30-second+ pauses on the MS JVM included with IE. It turned out that the only problem was that the garbage collector was running during these pauses. Garbage collection schemes are left up to the implementation of the JVM. Since you have little control over them (you can tell them to garbage collect on demand, at least), it is best to avoid turning control over to them unless absolutely necessary. Doing simple object pooling can enhance the performance of a program tremendously. For all you library developers out there, that means *don't* create immutable classes. Immutable classes prevent the use of object pooling.
  • Inserting a disclaimer here: I currently work for KL Group, the makers of JProbe.

    Anyway, KL has published some articles and delivered some lecture's about performance-tuning Java, particularly in the area of memory use.

    They are fairly helpful. Some of the points made in these articles are the same as what woggo made above, others are also useful information. These can be found off the JProbe [klgroup.com] page, but I've included two of them below.

    How do you plug Java Memory Leaks? [ddj.com] This was published in Dr. Dobbs Journal.

    Our CTO, Ed Lycklama, gave a talk entitled "Designing for Performance on the Java Platform" at JavaOne this year. There's a graphical [klgroup.com] and a text [klgroup.com] on the KL site.

Solutions are obvious if one only has the optical power to observe them over the horizon. -- K.A. Arsdall

Working...