HOME  |    TRAINING  |   FREE TUTORIALS   |   JOBS
Find out more about our new RSS feed.
FREE Tutorial
JAVA: PERFORMANCE TUNING AND MEMORY MANAGEMENT PART 2 - TIPS FOR IMPROVING PERFORMANCE

CATEGORY
SEARCH OUR OTHER TUTORIALS

DESCRIPTION

Once you've identified a performance problem, there are usually ways that you can address the problem. The most obvious solution is to upgrade the hardware that the application runs on, but this is often not possible, especially when large numbers of client machines are involved.
Click here to be kept informed of our new Tutorials.


This free tutorial is a sample from the book Professional Java Programming.


Another approach that doesn't involve changing code is to evaluate different Java Virtual Machine implementations when more than one exists for the operating system(s) onto which you will deploy your code. There is no single fastest JVM implementation for all applications, because most implementations have advantages in some areas. For example, Sun's JVM may be fastest for running the client portion of your application while IBM's implementation might be better for running the server portion. The factors that determine which JVM is the fastest for a given application are complex and difficult to predict, so the only reliable way to find out is by trying it.

If you must use a particular vendor's JVM, you should normally try to use the latest version, because as mentioned earlier in the chapter, the trend has been for JVM implementations to become faster over time. In other words, the chances are good that a later release of some vendor's JVM will execute your code more quickly than an earlier version of that same vendor's JVM.

At a minimum, you should ensure that the JVM implementation you're using includes a Just-In-Time (JIT) compiler or some other type of compiler such as JavaSoft's HotSpot, since these can greatly improve your application's performance. JIT compilers and the HotSpot compiler are described later in this chapter.

When you've identified a specific portion of your code that's responsible for a performance problem, you may be able to use one or more of the following tips to modify or rewrite that code so that it executes more quickly.

Use Native Methods

If you're unable to modify a method so that it runs quickly enough, you should consider rewriting the method using a faster language such as C or C++. By doing so, you sacrifice your application's ability to run unchanged on multiple platforms, but if you're reasonably certain that it will be run only on a single operating system this may be a worthwhile tradeoff. There is a small amount of overhead involved in transferring control from the JVM to a native code (and vice versa), but it is usually outweighed by the improved speed of the native function. See Chapter 20 for more information on using native methods.

Use Buffering on I/O Operations

As described in Chapter 13, you should use the BufferedInputStream, BufferedOutputStream, BufferedReader, and BufferedWriter classes, as these will reduce the number of I/O operations performed.

Avoid Creating New Objects

The new operation is relatively slow, so you should avoid creating objects as much as possible. In some cases, it may be possible to reuse existing objects, and it's generally preferable to do so when possible. For example, if you're performing a large number of String manipulations, you may have the option of reusing an instance of StringBuffer instead of creating more than one instance.

Use StringBuffer Instead of String Concatenations

This is really a variation on the previous recommendation, since concatenating instances of String together involves the creation of new objects (String instances). That's necessary because String instances are immutable, so only by creating a new instance can you concatenate two strings together. For example, suppose that you have the following code:

String[] lines;
// ...
String result = "";
for (int i = 0; i < lines.length; i++) {
result += lines[i];
}

Each time the loop is executed, a new instance of String is created and its value assigned to the result variable. A much more efficient implementation of this same code would be one that uses a StringBuffer instead. Unlike String, StringBuffer is mutable, and true concatenations are possible as shown below:

String[] lines;
// ...
StringBuffer buffer = "";
for (int i = 0; i < lines.length; i++) {
buffer.append(lines[i]);
}
String result = buffer.toString();

In practice, using StringBuffer in a scenario similar to this may or may not improve your application's performance, although it is very unlikely to actually make your code run more slowly. The reason it may not help is that the Java Language Specification allows Java compilers to automatically compile code that uses String instances into bytecodes that use StringBuffer instead. In other words, your compiler may already be causing your application to use StringBuffer even if you only use String in your source code.

Avoid Synchronized Methods and Blocks

A call to a synchronized method takes longer to complete than a call to an unsynchronized method. On the surface, it might seem like a good idea to simply make all methods synchronized for thread safety, but this can cause your application to run more slowly.

Use ArrayList and HashMap Instead of Vector and Hashtable

This is a variation of the previous recommendation. The Vector and Hashtable classes are heavily synchronized for thread safety, while ArrayList and HashMap (which provide similar functionality) are not. Therefore, if speed is important within your application, you should consider using the new collection classes, which are not thread-safe, instead of their older synchronized equivalents.

If a thread safe collection is required within your code, the Collections class contains static methods that create a thread-safe wrapper around a collection.

Use Resource Pooling for Threads, Database, and Network Connections

As mentioned earlier, creating an object is a relatively time-consuming operation that should be avoided when possible. However, creating threads can also be slow, and creating network connections and database connections (which often use network connections) is usually very slow. Instead of having your application repeatedly create these types of connections, you should consider obtaining or creating a resource pool manager that allows you to reuse existing objects.

Make Methods Final, Static, or Private

To understand why final, static, and private methods execute more quickly than methods without those modifiers, let's examine how Java behaves in a simple situation. Suppose you're given the following code:

public class Test {

public static void main(String[] args) {
 First o1 = new First();
 First o2 = new Second();
 if (args.length > 0) {
  callTest(o1);
 } else {
  callTest(o2);
 } 
} 

protected static void callTest(First f) {
 f.testMethod();
} 

}

class First {

public void testMethod() {
 System.out.println("First Implementation");
} 

}

class Second extends First {

public void testMethod() {
 System.out.println("Second implementation");
} 

}

This code defines two classes called First and Second, and Second is a subclass of First and overrides testMethod(). The Test class creates an instance of each of those classes and passes one of them to its callTest() method based on whether any command line parameters were specified. If no parameters are specified, an instance of Second is created and passed to callTest(), while specifying parameters will result in a First object being used instead.

Since you're probably already familiar with inheritance and method overriding, the behavior of this class shouldn't come as a surprise to you. However, you may not have previously considered how Java is able to provide this functionality. In particular, it's worth explaining how Java is able to determine which testMethod() to call when an instance of First is passed to callTest(). After all, the instance of First can actually be an instance of Second, since that class is a subclass of First.

As you might expect, Java determines at execution time which implementation of testMethod() should be executed, which is necessary because there's no reliable way to know at compile time. This approach, where the method implementation is selected at runtime, is known as dynamic binding, and although it provides very useful functionality, it comes at a price. Specifically, dynamic binding is slower than static binding, where the implementation to execute is selected at compile time.

It's sometimes not appropriate to do so, but you can allow Java to use static binding (and thus speed up the method calls) by making methods final, static, and/or private. A method with one or more of these modifiers cannot be overridden, so there is no need for Java to determine at runtime which implementation to execute.

Minimize Subclasses and Method Overriding

An alternative approach to reducing the overhead associated with binding is to simply reduce the number of subclasses and overridden methods that your application uses, although doing so may affect the reuse potential of your code. Creating reusable code usually requires that you define classes that are loosely coupled to one another and cohesive, which in turn tends to promote the creation of small, focused classes and the implementation of extra functionality through subclasses.

Unfortunately, each superclass "level" that you add increases the overhead associated with dynamic binding, so you may need to balance your need for an effective object-oriented design with your need for an application that executes quickly. However, you should be aware that the overhead associated with dynamic binding is relatively small, and only in the most performance-critical situations should you consider sacrificing design quality for performance.

Continued...


NEXT PAGE



7 RELATED COURSES AVAILABLE
INTRODUCTION TO JAVA PROGRAMMING
The aims of this Java training courses is to understand the role that Java plays on the Internet; describe the be....
JAVA (V1.2): ADVANCED PROGRAMMING
This course teaches the reader to learn, understand and become familiar with the advanced features of Java progra....
INTRODUCTION TO JAVABEANS
To go from the fundamentals of JavaBeans programming to the threshold of Advanced level. Gaining in depth progr....
C++ PROGRAMMING
Object oriented programming is fast becoming the leading software design methodology, with C++ becoming ever more....
C PROGRAMMING
This course is design to provide non-C programmers with the essential skills and knowledge necessary to allow the....
 
1 RELATED JOBS AVAILABLE
JAVA DEVELOPER MANCHESTER
Computer Futures Solutions are seeking a Senior Java Developer for their Manchester based client. You will be joi....
CONTACT US
Saturday 22nd November 2008  © COPYRIGHT 2008 - VISUALSOFT