As Published In
Oracle Magazine
September/October 2005

DEVELOPER: Optimization


Building Faster Java

By Harshad Oak

Optimize your Java applications with Oracle JDeveloper 10g tools.

What if your Java IDE did more than just help you write code? What if it also gave you meaningful advice about how to actually improve it? What if you had a single, integrated environment that not only helped you optimize your Java applications, but also offered powerful built-in profiling, debugging, and optimization tools?

Oracle JDeveloper 10g does all that and more. It lets you develop, debug, deploy, check, and optimize your Java applications directly from within the IDE.

In this article, we'll look at the Java optimization tools that come with Oracle JDeveloper 10g and show how to use them to develop powerful, robust applications.

Java Optimization

Why is Java optimization so important? Throughout its history, Java has always endured claims of being slow. Although factors such as better hardware, better JVMs, and improvements to the Java language have drastically improved Java performance over the years, it still has a reputation of being slow. So for Java applications, performance is always a key issue.

But development teams can't go through every line of code to optimize Java applications. Instead, they specify unambiguous guidelines and programming standards at a project's inception, and then monitor and modify code using optimization tools such as those provided with Oracle JDeveloper 10g.

Oracle JDeveloper 10g can help teams perform a variety of optimization tasks such as fixing coding errors, avoiding common performance pitfalls, and optimizing or eliminating resource-hungry code. Here's a brief look at the optimization tools integrated into Oracle JDeveloper 10g

  • CodeCoach: Offers suggestions to help write better Java code

  • Code Audit: Checks code for adherence to programming standards

  • Code Metrics: Provides metrics about your code and identifies areas where it exceeds acceptable limits

  • Memory Profiler: Analyzes your application's memory usage

  • Execution Profiler: Helps you analyze your application's performance

  • Event Profiler: Helps you track specific application events

All of these tools are included within Oracle JDeveloper 10g; they don't require a separate download or installation.

Let's look at each tool in more depth. You'll start by creating a simple Java application, and then use it to show what these tools can do.

Creating the Java Application

This application was developed using Oracle JDeveloper 10g (10.1.3) Developer Preview (J2EE Download), which you can download at oracle.com/technology/products/jdev. When you install this version, you can use either your existing Java SDK installation or the Java SDK that comes with the Oracle JDeveloper install. If you use the former, when you first launch Oracle JDeveloper it will ask you, "Do you want to install OJVM in this JDK to take advantage of the profilers, CodeCoach, and advanced debugging features?" Click Yes to enable the features described in this article.

Now, let's create a new workspace and project to manage your work. From the main menu choose File -> New.... In the New Gallery dialog box, select the Workspaces node (under Categories: General), select the Application item, and click OK . In the Create Application dialog box that follows, type OraMag as the Application Name and click the Options >> button to show extended options. Type oramag as the Application Package Prefix, and select Java Application [Java, Swing] as the Application Template. Click OK .

You should now have a new workspace named OraMag and a project named Client within that workspace. From the Applications Navigator window, right-click the Client project node, and select New . In the New Gallery dialog box, select the General node, then the Java class item, and then click OK . In the Create Java Class dialog box, enter OptimizationTrial for the class name and oramag.client for the package name. Click OK to generate the new OptimizationTrial class file.

Now replace the code in the class with the code shown in Listing 1. This test application simply outputs the current day, and then adds elements to two ArrayList objects. Once the code is in place, you'll use each tool to analyze and improve what you've written.

Code Listing 1: OptimizationTrial.java before first CodeCoach run 

package oramag.client;
import java.io.IOException;

import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Date;

public class OptimizationTrial {
    public OptimizationTrial() {
    }

    static int count=99999;
    
    public static void main(String[] args) throws IOException{
        todaysDay();
        demoArrayListOptimized(count);
        demoArrayList(count);
    }
    
    public static void todaysDay() throws IOException{
        Object today = fetchTodaysDay();
        if (today instanceof String) {
System.out.println("The object 'today' is always a String. Check unnecessary");
        }
        
        System.out.println("Today is >>>" + today);
    }    
    
    public static Object fetchTodaysDay() {
        SimpleDateFormat sfInput = new SimpleDateFormat("EEEEEEEEE");
        return sfInput.format(new Date());
    }
    
    public static void demoArrayList(int number) {
      System.out.println("Unoptimized Run");
      ArrayList ar= new ArrayList ();
      //NOTE: Change number to zero to demonstrate codecoach and audit features
      for(int i=0; i<0; i++) {
        ar.add(new Integer(i));
      }
    }
    
    public static void demoArrayListOptimized(int numberOpt) {
      System.out.println("Optimized Run");
      ArrayList ar= new ArrayList (numberOpt);
      for(int i=0; i<numberOpt; i++) {
        ar.add(new Integer(i));
      }
    }      
}


Using CodeCoach

CodeCoach helps you avoid well-known Java pitfalls and write better Java code. It highlights problems in your code that need to be fixed and provides other code-improvement suggestions. You can use it with any Java or J2EE project.

CodeCoach offers the following types of advice: 

  • Class advice: Should a class be declared final or static?

  • Method advice: Should a method be declared final, private, or static?

  • Field advice: Is there an unused field, or should a field be declared final, local, private, or static?

  • Local variable advice: Is a local variable unused or should it be declared final?

  • Memory improvement advice: General advice related to the usage of ArrayList, BitSet, HashMap, Hashtable, StringBuffer, Vector , and WeakHashMap classes.

 

Configure CodeCoach from the main menu by choosing Tools->Preferences and then selecting the CodeCoach node. You can set the level of advice provided from 1-10, where 1 indicates only a few types of advice and 10 indicates all types of advice. You can also specify classes and packages to include or exclude during its operation.

To run CodeCoach from the main menu, choose Run->CodeCoach Client.jpr .

Try it now. First, from the main menu, choose Tools->Preferences , select the CodeCoach node, set the advice level slider to 10, and click OK . Then run CodeCoach on your Client.jpr project. The results are shown in Figure 1. CodeCoach provides 10 recommendations to improve your code, with the line number and the advice type shown for each recommendation. It finds a number of important code errors, including the unnecessary usage of instanceof and a listing of unused variables.

 

figure 1
Figure 1: CodeCoach results for code in Listing 1


Another important CodeCoach suggestion is the "dead code" suggestion for demoArrayList() . Looking at the code, it's clear that you inadvertently wrote i < 0 instead of i < number when constructing the for loop. As a result, this dead code will never get executed. Dead code isn't always obvious even to human reviewers, so CodeCoach's ability to find this type of error is especially useful.

In many cases, you can have CodeCoach automatically fix the errors it finds by simply right-clicking on the error and selecting Apply Fix To Source . In other cases (such as with the dead code suggestion), you'll need to make the appropriate changes yourself. The updated code listing, which corrects all the problems found by CodeCoach, is shown in Listing 2.

Code Listing 2: OptimizationTrial.java after CodeCoach optimization 

package oramag.client;
import java.io.IOException;

import java.text.SimpleDateFormat;

import java.util.ArrayList;
import java.util.Date;

public final class OptimizationTrial {
    public OptimizationTrial() {
    }

    private static final int count=99999;
    
    public static void main(String[] args) throws IOException{
        todaysDay();
        demoArrayListOptimized(count);
        demoArrayList(count);
    }
    
    private static void todaysDay() throws IOException{
        Object today = fetchTodaysDay();
        //if (today instanceof String) {
System.out.println("The object 'today' is always a String. Check unnecessary");
        //}
        
        System.out.println("Today is >>>" + today);
    }    
    
    private static Object fetchTodaysDay() {
        SimpleDateFormat sfInput = new SimpleDateFormat("EEEEEEEEE");
        return sfInput.format(new Date());
    }
    
    private static void demoArrayList(int number) {
      System.out.println("Unoptimized Run");
      ArrayList ar= new ArrayList ();
      //NOTE: Change number to zero to demonstrate codecoach and audit features
      for(int i=0; i<number; i++) {
        ar.add(new Integer(i));
      }
    }
    
    private static void demoArrayListOptimized(int numberOpt) {
      System.out.println("Optimized Run");
      ArrayList ar= new ArrayList (numberOpt);
      for(int i=0; i<numberOpt; i++) {
        ar.add(new Integer(i));
      }
    }      
}


Now run CodeCoach on this updated code in Listing 2. The new results are shown in Figure 2. In analyzing your use of the ArrayList object in demoArrayList() , CodeCoach determines that too many expansions of ArrayList are taking place. How did it find this problem? Compare the methods demoArrayList() and demoArrayListOptimized() in Listing 2. In demoArrayList() , you didn't state the initial size of the ArrayList , so it will initially be created with a small size but grow every time you add a new item beyond its current size. However, in the optimized method demoArrayListOptimized() , you specified the size of the ArrayList up front. Since it's clear from the code that you're adding 99,999 items to the ArrayList, the optimized method takes less time and memory than the unoptimized method. (We'll compare the performance of these two methods later in this article.)

 

figure 2
Figure 2: CodeCoach results for code in Listing 2 (optimized)


You've seen how CodeCoach can help you write better code. Now let's look at how Code Audit can save you precious hours otherwise spent manually reviewing Java code.

Using Code Audit

While CodeCoach analyzes and suggests changes to code based on how it performs on execution, Code Audit performs a static audit of the code without needing to actually execute it. Code Audit can complement peer code review by allowing peer reviewers to focus on the business logic specific to that code, while Code Audit automates certain general, low-level review tasks. For example, Code Audit can find programmer errors such as missing Javadoc comments or nonadherence to naming conventions.

You can configure the level and type of audit performed by selecting an appropriate audit profile under the Tools-> Preferences menu. To do so, from the main menu select Tools->Preferences , expand the Audit node, and select Profiles . In addition to selecting a particular profile, you can specify individual rules to check for, such as missing Javadoc, nonadherence to naming standards, and improper exception handling.

You can run Code Audit on your code from Listing 1 from the main menu by choosing Run->Audit OptimizationTrail.java . In the dialog box that appears, select the All Rules profile, and click Run . The results are shown in Figure 3. Code Audit finds eight low-priority Javadoc violations and three medium-priority violations. In some cases, you can have Code Audit auto-fix these violations by right-clicking an item and selecting Apply... from the menu that appears. In other cases, you'll need to apply changes manually.

 

figure 3
Figure 3: Code Audit results for code in Listing 1


Let's look at Code Metrics, which is now integrated within the Code Audit user interface.

Using Code Metrics

The Code Metrics tool performs a quantitative analysis of your code. You can use it to measure the following: 

  • The depth of the inheritance tree (DIT): DIT refers to the level of the Java inheritance hierarchy in your code. In Listing 1, your class extends the java.lang.Object class, which is at the top of the Java hierarchy, so the DIT is 2.

  • Cyclomatic complexity (V(G)): This value provides a measure of the logical complexity of a program, and depicts the branching complexity of a class or method. A high V(G) value can raise a flag to help you find areas of significant complexity in your code.

  • Number of Statements (NOS): This attribute specifies the number of Java statements in a method, class, or other construct.

 

You run Code Metrics similarly to Code Audit. From the main menu, choose Run->Audit OptimizationTrial .java , and select All Metrics in the resulting dialog box. If you run Code Metrics with the code in Listing 1, you'll get the results shown in Figure 4. Since your code is pretty straightforward, all the values shown in Figure 4 are within acceptable limits.

 

figure 4
Figure 4: Code Metrics results for code in Listing 1


Code Metrics can help you enforce coding rules that you specify across your development teams. For example, if you set a rule that no method in an application should be more than x number of lines and inheritance should never be more than y levels, you can easily check adherence using Code Metrics. These kinds of rules can help make sure that your developers maintain a certain level of code quality throughout the application.

You configure Code Metrics in the same way you configure Code Audit: from the main menu, choose Tools-> Preferences , expand the Audit node, and then choose Profiles . To set Code Metrics, select All Metrics in the dialog box and set the DIT, V(G), and NOS values as needed. With both Code Audit and Code Metrics, you can select the rules that the tool should check for, as well as create new profiles that capture your preferred selections.

Now let's look at some of the profiler tools in Oracle JDeveloper 10g that can help you better understand your code's performance.

Profilers in Oracle JDeveloper 10g

Oracle JDeveloper 10g provides three profiling tools: Memory Profiler, Execution Profiler, and Event Profiler. We'll look at the first two profilers in this article. (Event Profiler comes with built-in events that it can track, and allows you to insert calls to the Profiler API in your code to track your own events. For information on Event Profiler, refer to the link in the Next Steps box at the end of this article.)

Profiling activities can overwhelm users with too much data and thus end up accomplishing very little. To get the best use out of profiling tools, then, it's important to not try to optimize every line of Java code in your application. In most cases, it's usually a small portion of code that takes the most time and consumes the most resources. So one worthwhile strategy is to optimize these code snippets.

One other note about profilers: the output may change based on your computer configuration. If you have a very fast computer that can execute our sample code in less than 0.5 seconds, Oracle JDeveloper 10g won't be able to generate accurate reports because the minimum update interval is 0.5 seconds for the Memory Profiler and 5 milliseconds for the Execution Profiler. This issue generally won't be a problem for complex, real-world applications that take a lot more time to execute than our simple example.

Using the Memory Profiler

The Oracle JDeveloper 10g Memory Profiler provides a visual analysis of your application's memory usage. It offers many memory-related details, such as the number of instances created and the size of your Java objects. It also captures snapshots at specified durations to let you see how memory consumption changes over the life of the application. You can also configure the Memory Profiler to monitor classes from only specific packages, to help focus your analysis. These capabilities are essential to help you tune performance, optimize memory usage, and make sure that unnecessary objects don't bog down the system by consuming too much memory.

Let's start from the main menu by choosing Tools->Preferences, expanding the Profiler node, and selecting Memory . Change the Update Interval to 0.5 seconds, and click OK . Note that Oracle JDeveloper 10g gives you an option to take samples manually in addition to auto-sampling.

Next, load the code from Listing 2, and comment out the line demoArrayListOptimized(count) ; in the main method. Now run the Memory Profiler from the main menu by choosing Run-> Memory Profile Client.jpr . The output will be similar to what's shown in Figure 5. As you can see, the Memory Profiler gives detailed information such as the number of instances in the heap, their current size in the heap, the number of instances allocated after the last sample was taken, and the size of these instances. You can sort your data based on any of the columns in the list, or you can save the report to an HTML file.

 

figure 5
Figure 5: Memory Profiler Output


Since our application spends most of its time adding integers to an ArrayList , you'll see that the numbers for java.lang.Integer will top the list.

Now, uncomment the line demoArrayListOptimized(count); and comment the call to the method demoArrayList(count); on the very next line. Then, run the Memory Profiler again. You'll see a significant drop in the total current size in the heap, primarily because of a drop in the size of java.lang.Object[] .

You can also use the Memory Profiler to check if a particular coding approach is memory-intensive or if instances of a certain class are taking up too much memory. The Count field can tell you if too many instances of your custom classes or data objects are still hanging around after they've been used.

Next Steps


READ
about Event Profiler

DOWNLOAD
Oracle JDeveloper 10g

For the last stop of our tour, let's look at the Execution Profiler.

Using the Execution Profiler

The Execution Profiler is used to time your application. It shows how much time the application spends in each method, giving you valuable information to help you determine which ones you should optimize.

You can run the Execution Profiler from the main menu by choosing Run->Execution Profile Client.jpr . The results show how long each method took, as well as the methods called from that method and how long those methods took. So the next time you think a particular flow in your application seems unacceptably slow, try monitoring it with the Execution Profiler and then optimize the methods that are the most sluggish.

Let's do the same experiment we did with Memory Profiler. First, load the code in Listing 1, then comment out the call to the method demoArrayListOptimized() .When you run the Execution Profiler (by choosing Run->Execution Profile Client.jpr) , you'll get output similar to that shown in Figure 6. Then uncomment demoArrayListOptimized() , comment out demoArrayList() , and run the Execution Profiler again. Note the drop in total execution time, and also note that the method demoArrayListOptimized() takes a lot less time than the unoptimized method demoArrayList() .

 

figure 6
Figure 6: Execution Profiler Output


Optimize Early

We've looked at a number of Oracle JDeveloper 10g tools that give you valuable data that can help you improve the quality of your Java code and develop top-performing applications. I recommend you learn how to use these tools and incorporate code optimization activities early in your project lifecycles. As we all know, when schedules get very tight, code reviews and optimization tasks are often the first casualties in the development process.


Harshad Oak (harshad@rightrix.com) is the founder of Rightrix Solutions. He's the author of Oracle JDeveloper 10g: Empowering J2EE Development (Apress, 2004) and Pro Jakarta Commons (Apress, 2004), and coauthor of Java 2 Enterprise Edition 1.4 Bible (Wiley & Sons, 2003).


Send us your comments