Make Your Java Applications Run Faster - Part 4 - Methods, Synchronization, Instantiation, Casting, Exceptions and Threads
In this final part of the series "Make Your Java Applications Run Faster Part" we shall see how you can optimize Methods, Synchronization, Instantiation, Casting, Exceptions and Threads.
Method Call Optimization: There are two basic categories of methods: those that can be statically resolved by the compiler and the rest, which must be dynamically resolved at run time. To statically resolve a method, the compiler must be able to determine absolutely which method should be invoked. Methods declared as static, private, and final, as well as all constructors, may be resolved at compile time because the class to which the method belongs to is known. A statically resolved method executes more quickly than a dynamically resolved method because, statically resolved method, the binding is done by the compiler and for other methods the binding should happen at runtime depending on the object instance that is being refered.
Even for a statically resolved methods, there will be considerable execution overhead – the local variables should be saved, the method arguments should be pushed to the stack. So it is better to eliminate small methods if there is only one caller. You may not be able to eliminate method calls in your code, but you can at least minimize the method calls at the bytecode level by inline the methods. You can instruct the compiler to inline small methods by using the –O flag.
Inlining a method call inserts the code for the method directly into the code making the method call. This eliminates the overhead of the method call. For a small method this overhead can represent a significant percentage of its execution time. Note that only methods declared as either private, static, or final can be considered for inlining, because only these methods are statically resolved by the compiler. Also, synchronized methods won't be inlined. The compiler will only inline small methods typically consisting of only one or two lines of code.
The next best option is to convert method and especially interface invocations to static, private, or final calls. If you have methods in your class that don't make use of instance variables or call other instance methods, they can be declared static. If a method doesn't need to be overridden, it can be declared final. And methods that shouldn't be called from outside the class should always be declared private anyway.
Minimize Synchronization: Call to a synchronized method is very expensive, in fast it is the most expensive method call. Not only does it involve getting the lock on an object’s monitor but, but there is always the potential that the call will be delayed waiting for the monitor for the object. And when the method returns, the monitor must be released, which takes more time.
So, it is always better to reduce usage of synchronized methods and blocks as far as possible, so make sure that the operations actually require synchronization. Be careful though; this is not an area of your program to be overly ambitious in optimizing as the problems you can cause can be difficult to track down.
Also, you must try to use synchronized methods in place of synchronized blocks as far as possible because synchronized method invocation is slightly faster than entering a synchronized block as synchronized method invocation is handled differently in the byte code.
Also, a call to a synchronized method when the monitor is already owned by the thread executes somewhat faster than the synchronized method when the monitor isn't owned. So, If you have a heavily synchronized class that is calling lots of synchronized methods from other synchronized methods, you may want to consider having a few synchronized methods delegate the work to private non-synchronized methods to avoid the overhead of reacquiring the monitor.
Save Casting: Casting object references to refer to different object types in Java (object casts) can get pretty dense -- especially if you work with a lot of lists or other collection classes. It turns out that any object cast that can't be resolved at compile time is so expensive that that it is better to save the cast object in a local variable than to repeat the cast.
So instead of writing:
boolean equals (Object obj) {
if (obj instanceof Rectangle)
return (((Rectangle)obj).x == this.x && ((Rectangle)obj).y == this.y && ((Rectangle)obj).width == this.width && ((Rectangle)obj).height == this.height);
return false;
}
Do the casting only once:
boolean equals (Object obj) {
if (obj instanceof Rectangle) {
Rectangle rect = (Rectangle)obj;
return (rect.x == this.x && rect.y == this.y && rect.width == this.width && rect.height == this.height);
}
return false;
}
If the cast is to an interface, it's probably twice slower than casting to a class.
In fact, there is one type of cast that can take much longer to execute. If you have an object hierarchy like
interface I {}
class Super {}
class Sub extends Super implements I {}
then the following cast, to an interface implemented by a subclass, takes anywhere from two to three times as long as casting to the subclass.
Super su = new Sub();
I i = (I) su;
The further the separation between the interface and the subclass (that is, the further back in the interface inheritance chain the cast interface is from the implemented interface), the longer the cast takes to resolve.
Also beware of unnecessary uses of instanceof. The following cast is resolved by the compiler and produces no runtime code to implement the unnecessary cast.
Rectangle q = new Rectangle ();
Rectangle p = (Rectangle) q;
However,
Rectangle p = new Rectangle ();
boolean b = (p instanceof Rectangle);
cannot be resolved by the compiler because instanceof must return false if p == null.
Casting data types is simpler and cheaper than casting objects because the type of the data value being cast is known (for example, an int never is actually a subclass of an int.) However, again since int is the natural size used by the JVM (ints are the common data type that all other data types can be directly converted to and from), beware of using the other data types. Casting from a long to a short requires first casting from a long to an int and then from an int to a short.
Reuse Object Instances: Creating an object is fairly expensive in terms of CPU cycles. On top of that, discarded objects will need to be garbage collected at some point. It takes about as long to garbage collect an object as it takes to create one.
Also, the longer the hierarchy chain for the object, the more constructors that must be called. This adds to the instantiation overhead. If you add extra layers to your class hierarchy for increased abstraction, you'll also increase the instantiation time.
The best option is to avoid instantiating objects in tight loops. Where possible, reuse an existing object.
The loop
for (int i = 0; i < limit; i++) {
ArrayList list = new ArrayList();
// do something with list...
}
would be faster if written as
ArrayList list = new ArrayList();
for (int i = 0; i < limit; i++) {
list.clear();
// do something with list...
}
Also, it is better not to explicitly initialize instance variables to their default values because when an objects is created all instance variables will by virtue get initialized to the default values. If you are explicitly doing the same, it will duplicate the default initialization, generate additional bytecodes, and make the instantiation take longer. (There are rare cases when the explicit initialization is needed to reinitialize a variable that was altered from the default value during the super class's constructor.)
Optimizing Exception Handling: The try/catch/finally mechanism is implemented efficiently in the JVM. When an exception is thrown, only a quick check is done against the exception table for each method in the call chain to determine whether or not the exception is handled by the method.
Also, a try {} statement is cheap, no much bytecodes are generated, all that try {} statement adds to your code as far as overhead is the goto bytecode to skip the catch () {} statement and one or more entries in the method's exception table. A finally statement adds a little more overhead as it is implemented as an inline subroutine.
However, instantiating a new exception can be expensive. An exception generates and stores a snapshot of the stack at the time the exception was instantiated. This is where most of the time for instantiating an exception goes. If you're throwing exceptions regularly as part of the normal operation of your code, you may want to rethink your design.
But, if you do have a need to do this (such as returning a result from several calls deep), and if you're simply going to catch and handle the exception, then repeatedly instantiating an exception can be a waste of time. Instead, it is possible to reuse an exception. That is when you are not concerned with logging the exception, but you are only interested in returning a result from several calls deep, then you can have the exception as a class variable and always change the required values in the exception and throw the reused exception.
Reduce Threads: Using threads can cause problems memory wise and also CPU cycle wise. Each running thread has both a native/"C" stack and a Java stack which will take some memory. There is also an overhead for switching between threads (a context switch), which varies significantly between platforms. Moreover, if the threads are in some synchronized method or block, then a context switch also involves releasing / acquiring the monitor for an object, so context switch will be much slower when threads are in synchronized methods or blocks.
There are many places in your code where threads can be minimized or avoided. For Example, a java game can be easily implemented by using separate threads for painting, keys handling and AI, In fact, Painting, key handling and AI together can be handled by a single thread make will make the code slightly complex but will avoid the over associated to context switching between threads..