JDK 1.1 for Solaris Developer's Guide

System Interface Level

These areas of the Java system interface level, where tuning can often result in significant performance gains, are discussed here:

Compiler Optimization Level

The optimizations for these compilers are listed as:

Code Tuning Level

Code tuning in these areas can be used to increase performance:

I/O Issues

The biggest and most common performance problem in Java applications is often inefficient I/O. Therefore, I/O issues should generally be the first thing to look at when performance-tuning a Java application. Fixing these problems often results in greater performance gains than all the other possible optimizations combined. It is not unusual to see a speed improvement of one order of magnitude achieved by using efficient I/O techniques.

If an application performs a significant amount of I/O, then it is a candidate for I/O performance tuning. This conclusion can be confirmed by profiling the application. To learn how to profile an application, you can use the Java WorkShop (JWS) product. JWS can be obtained from:

http://www.sun.com/workshop

Select Help->Help Contents, and click on Profiling Projects. This example involves running a benchmark test reading a 150,000-line file using four different methods:

  1. DataInputStream.readLine() alone (unbuffered)

  2. DataInputStream.readLine() with a BufferedInputStream underneath, which has a buffer size of 2048 bytes

  3. BufferedReader.readline() with a buffer size of 8192 bytes.

  4. BufferedFileReader(fileName)

The results were as follows: (times in seconds) :

DataInputStream: 178.740
DataInputStream(BufferedInputStream): 21.559
BufferedReader 11.150
BufferedFileReader  6.991

Note that methods 1 and 2 do not properly handle Unicode characters, while methods 3 and 4 handle them correctly. This makes methods 1 and 2 unacceptable for most product uses. Also, DataInputStream.readLine() is deprecated as of JDK 1.1. Method 1 is used in JWS and other programs.

Another way to spot Solaris I/O problems is to use truss(1) to look for read(1) and write(1) system calls.

Strings

When using strings, the most important thing to remember is to use char arrays for all character processing in loops, instead of using the String or StringBuffer classes. Accessing an array element is much faster than using the charAt() method to access a character in a string. Also, remember that string constants ("...") are already string objects.

//DON'T

String s = new String("hello");

//DO

String s = "hello";

In addition:

Arrays

Arrays are bounds-checked, which will degrade performance. However, accessing arrays is much faster than accessing Vector, String, and StringBuffer. Use System.arraycopy() to improve performance. This is a native method, and much faster than manual array processing.

Vectors

Vector is convenient to use, but inefficient. For best performance, use it only when the structure size is unknown, and efficiency is not a concern. When using Vector, ensure that elementAt() is not used inside a loop, as performance will degrade. Use Vector only when you have an array with the following characteristics:

Hashing

HashTable has these tunable parameters:

Images

You can use the following types of images.

Painting and Drawing

To improve performance in these areas, use the following techniques:

Asynchronous Loading

To improve (asynchronous) loading performance, use your own imageUpdate() method to override imageUpdate(). imageUpdate() can cause more repainting than you might want..


//wait for the width information to be loaded
while (image.getWidth(null) == -1 {
		try {
			Thread.sleep(200);
		}
		catch(InterruptedException e) {
		}
	}  
	if (!haveWidth) {
		synchronized (im) {
			if (im.getWidth(this) == -1) {
				try {
					im.wait();
         	}
        	catch (InterruptedException) {
				}
			}
		}
//If we got this far, the width is loaded, we will never go thru
// all that checking again.
		haveWidth = true;
	} 
... 
public boolean imageUpdate(Image img, int flags, int x, int y, int width, \
			int height) {
		boolean moreUpdatesNeeded = true;
		if ((flags&ImageObserver.WIDTH)!= 0 {
			synchronized (img) {
				img.notifyAll();
				moreUpdatesNeeded = false;
			}
		}
		return
		moreUpdatesNeeded;
}    

Pre-Decoding

Pre-decoding and storing the image in an array will improve performance. Image decoding time is greater than loading time. Pre-decoding using PixelGrabber and MemoryImageSource should combine multiple images into one file for maximum speed. These techniques are more efficient than polling.

Memory Usage

You can dramatically improve application performance by reducing the amount of garbage collection performed during execution. The following practices can also increase performance:

Threads

As discussed in "Java Threads in the Solaris Environment -- Earlier Releases*", performance is increased dramatically by using native threads. Green threads are not time-sliced and might require calls to Thread.yield() in loops, slowing execution. Other techniques to avoid:

Compiler Optimizations

The following compilers automatically perform the listed optimizations.

Java Compiler

JIT Compiler

Code Optimization

Loops

Use these techniques for performance improvements:

Convert expr to Table Lookup

When a value is being selected based on a single expression with a range of small integers, convert it to a table lookup. Conditional branches defeat many compiler optimizations.

Caching

Though caching takes more memory, it can be used for performance improvement. Use the technique of caching values that are expensive to fetch or compute.

Precompute Results

Increase performance by precomputing values known at compile time.

Lazy Evaluation

Save startup time by delaying computation of results until they are needed.

Class as Opposed to Object Initialization

Speed performance up by putting all one-time initializations into a class initializer.