Java Performance Tips 1



15. Libraries

This section considers on some of the performance issues of using classes and methods from the standard Java API libraries.

15.1 System.arraycopy()

To copy an array into another other, the best style is using System.arraycopy() method and is very efficient. In the following program, array copying is done using three styles – using a loop, using arraycopy() method of System and using clone() method of Object class.

public class CopyArray
{
  public static void main(String args[]) 
  {
    int array1[] = new int[1000000];   // create two integer arrays
    int array2[] = new int[1000000];

    for (int i = 0; i < 1000000; i++)     // populate first array, array1
    {
      array1[i] = i;
    }
                      // copy array1 elements into array2 using loop
    Clock c1 = new Clock();
    for (int i = 0; i < 1000000; i++)
    {
      array2[i] = array1[i];
    }
    c1.printTime("Using loop");
			// copy using System.arraycopy()
    c1.setZero();
    System.arraycopy(array1, 0, array2, 0, 1000000);
    c1.printTime("Using arraycopy() method");
				// copy using Object.clone()
    c1.setZero();
    array2 = ( int[] ) array1.clone();
    c1.printTime("Using clone() method");
  }
}

Output screen of CopyArray.java
The cloning takes a long time. In cloning, a new memory location is created, the data from the object variable-by-variable is copied into the location.

As JVM uses a JIT compiler, the difference between the loop and arraycopy() is very less.

15.2 java.util package

It is a very big package with a number data storage classes with different options.

15.3 Vector vs. ArrayList

java.util.Vector is used to represent a collection of objects, with a facility of growing the capacity.

JDK 1.2 comes with newer class ArrayList that can be used as a substiture of Vector. A few of the performance differences between Vector and ArrayList are:

1. Vector's methods are synchronized, ArrayList’s are not. This means that Vector is thread-safe, at big extra cost.

2. LinkedList is an alternative for ArrayList but with various performance bottlenecks.

3. When vector capacity gets exhausted with the addition of elements, the capacity increases by 10 every time. ArrayList does not have this constraint. So ArrayList is more conservative in its use of space.

4. It is always better to use collection classes as all are not synchronized. A synchronized version can be obtained using Collections class as follows:

List list = Collections.synchronizedList(new ArrayList());

The returned list is thread-safe but the original list(passed as parameter) is still not synchronized.

Also refer Vector vs ArrayList for more information.

15.4 Setting Initial Array Capacity

Collection classes like ArrayList now and then should grow their capacity of internal data structure to facilitate to add more elements. Addition of more capacity when required is automatic and thereby programmer need not bother and takes more performance overhead. If the number of elements to be added to arraylist is known earlier (at compile time itself), an arraylist with the specific number of elements can be created while creating the arraylist object itself. This increases a lot of performance and is illustrated in the following program.

import java.util.*;
public class ArrayListCapacity
{
  public static void main(String args[]) 
  {
    Object obj = new Object();
            	                          // wihout using initial capacity
    ArrayList list = new ArrayList();
    Clock c1 = new Clock();
    for (int i = 0; i <1000000; i++)
    {
      list.add(obj);
    }
    c1.printTime("Wihout using initial cacpacity");
                                       // Using initial capacity        
    list = new ArrayList(1000000);
    c1.setZero();
    for (int i = 0; i <1000000; i++)
    {
      list.add(obj);
    }
    c1.printTime("Using initial cacpacity");
  }
}
             Output screen of ArrayListCapacity.java
Output screen of ArrayListCapacity.java

If initial capacity is not set, the array list size is increased dynamically many times and is very expensive.

15.5 ArrayList vs. LinkedList

There comes two classes ArrayList and LinkedList to store objects. Internally, ArrayList stores elements as an array form and LinkedList stores elements in node form. Due to their internal style of storing the elements, they come with different performance heads depending the nature of action performed like addition and retrieval.

In the following program elements are inserted at 0 position always and actual 0 position elements are pushed down.

import java.util.*;
public class ArrayListLinkedList
{
  public static void main(String args[])
  {                                            // Using ArrayList
    ArrayList al = new ArrayList();
    Clock c1 = new Clock();
    for (int i = 0; i < 25000; i++)
    {
      al.add(0, new Integer(i));
    }
    c1.printTime("Using ArrayList");
 			                      // Using LinkedList
    LinkedList ll = new LinkedList();
    c1.setZero();
    for (int i = 0; i < 25000; i++)
    {
      ll.add(0, new Integer(i));
    }
    c1.printTime("Using LinkedList");
  }
}
Output screen of ArrayListLinkedList.java
Output screen of ArrayListLinkedList.java

Inserting elements at the beginning of an ArrayList requires that all existing elements be pushed down. This is a very costly overhead. But inserting at the beginning of LinkedList is cheap, because the elements of the structure are connected with each other via linked nodes. when an element is added, the two neighbour nodes are disturbed but not all nodes.

import java.util.*;
public class RandomLookup 
{
  public static void main(String args[]) 
  {
    Object obj;
  			                // Retrieving elements from ArrayList
    ArrayList al = new ArrayList();     // create an array list
    for (int i = 0; i < 25000; i++)     // populate the array list
    {
      al.add(new Integer(i));
    }
    Clock c1 = new Clock();
    for (int i = 0; i < 25000; i++)     // reading all the elements one by one
    {
      obj = al.get(i);
    }
    c1.printTime("Reading elements from an array list");
				        // Retrieving elements from LinkedList
    LinkedList ll = new LinkedList();  // create a linked list
    for (int i = 0; i < 25000; i++)    // populate the linked list
    {
      ll.add(new Integer(i));
    }
    c1.setZero();
    for (int i = 0; i < 25000; i++)   // reading all the elements one by one
    {
      obj = ll.get(i);
    }
    c1.printTime("Reading elements from a linked list");
  }
}
Output screen of RandomLookup.java
Output screen of RandomLookup.java

Accessing the elements with ArrayList is less expensive than accessing elements with LinkedList.

ArrayList LinkedList
Adding elements Expensive Cheaper
Retrieving elements Cheaper Expensive

To retrieve the elements, it is advised to use Iterators with LinkedList.

16. Programming in terms of Interfaces

The example in the previous section illustrates an important point, namely, that there may be more than one "right" way to represent data. You might, for example, decide that an ArrayList is the best choice for some application, but then later decide that a LinkedList would in fact be a better choice. One way you can make changeovers like this easier to program is by using interface types, instead of actual types of classes that implement the interface. For example, if you say:

	List list = new ArrayList();

instead of saying

        ArrayList list = new ArrayList();

and you do likewise when declaring method parameters, as in

		void display(List list) {    }

instead of

        void display(ArrayList list) {    }

then you can use List everywhere in your programming, and you don’t have to worry too much about whether list is really an ArrayList or a LinkedList. In other words, you program in terms of functionality offered by interface types, and you can switch the actual implementation easily at some later point, for reasons of efficiency.

17. Wrappers

The Java libraries have a lot of wrapper classes, classes used to hold a value of a primitive type such as int or double as an object. For example, you can say:

	Integer obj = new Integer(25);

to create a wrapper class instance. This instance can then be assigned to an Object reference, have its type tested using instanceof, used with collection classes like ArrayList, and so on. One drawback, however, is that wrapper classes have some overhead, both in time and space costs. It's less efficient to extract a value from a wrapper instance than it is to use the value directly. And there are costs in creating all those wrapper instances, both time costs in calling new and constructors, and space costs with the overhead that comes with class instances (a very rough estimate might be that an instance of a class takes 15 bytes, in addition to the size of the actual instance variables).

18. Garbage Collection

The Java language uses a form of automatic memory management known as garbage collection. Memory for objects and arrays is allocated using new, and then reclaimed when no longer in use. Garbage collection is a process of detecting when an object no longer has any references to it, with the object’s memory then reclaimed and made available as free space. Normally, you don't need to worry about garbage collection, but there are cases where it's possible to help the process along a bit. For example, suppose that you have a stack of object references, along with a current stack index, and you pop an entry off the stack:

	Object pop() 
	{
	  return stack[stackp--];
	}

This works pretty well, but what happens to the slot in the stack that was just popped? It still has a reference to some object type, and the object may be very large (like a big array). An identical reference has been passed back to the caller of pop(), but that caller may quickly process and discard the reference, and thus it’s possible that the object reference could be garbage collected, except for one thing – there's still a reference to the object on the stack. So a better way to write this code is:

	Object pop() 
        {
	  Object tmp = stack[stackp];
	  stack[stackp--] = null;
	   return tmp;
	}

In other words, the reference has been set to null. If this was the last reference to the object, then the object is made available for reclaiming by the garbage collector.

This idea can be generalized. If you have a reference to a large, no-longer-used object, and the reference is one that will stay in scope for some time to come, then you might want to clear the reference explicitly, by setting it to null. For example, you might have a class instance that persists throughout the life of your application, and one of the instance's variables references a large object, an object you are no longer using.

5 thoughts on “Java Performance Tips 1”

Leave a Comment

Your email address will not be published.