Java Performance Tips 1



9. Thread Synchronization

Synchronization is a concept used with threads where you want to give the method access exclusively to an object of a class. We must synchronize the data when other objects should wait until the accessing object completes its job. For example, when one object updates the data, the other object should not be allowed to modify the data. Synchronization is a very big overhead to the system and should be avoided generally. We must synchronize sensitive or critical data only.

In the following program, two methods, one synchronized and the other non-synchronized are called.

public class SyncDemo
{
  public void update()  {  }
  public synchronized void update1()  {  }
  public static void main(String args[])
  {
    SyncDemo sd = new SyncDemo();
                 		   // calling non-synchronized method
    Clock c1 = new Clock();
    for( int i = 0; i < 1000000; i++)
    {
      sd.update();
    }
    c1.printTime("Non-synchronization call time");

			          // calling synchronized method
    c1.setZero();
    for( int i = 0; i < 1000000; i++)
    {
      sd.update1();
    }
    c1.printTime("Synchronization call time");
  }
}

Calling synchronized method is 75 times overhead than non-synchronized method.

Java permits to synchronize a whole method or a block of a method.

			// synchronizing the whole method
public synchronized void update(double balance)
{
  balance = balance - amount;             // important statement
  System.out.println("Hello 1");         // not important statement
}

Java permits to synchronize a part(block) of a method

public void update(double balance)
{
  synchronzied(this)
  {
    balance = balance - amount;    // only important is synchronized
  }
  System.out.println("Hello 1");
  }

Comparatively, the second style gives more performance.

This difference is important in some areas. The Vector and ArrayList works the same. Vector methods are synchronized, but ArrayList methods are not synchronized. For ordinary data, use ArrayList in place of Vector.

10. Java Performance Tips: Inner classes

A class declared in another class is called nested class or inner class. As the specification says, the inner class can access the private data of outer class. But it is against the rules of Java; one call private properties cannot be accessed by other classes.

In the following program, OuterOne is outer class and the InnerOne is inner class. The private method calculate() of outer class is accessed by inner class constructor. Let us see the code and then we will discuss how Java executes the code of private property of outer class by inner class.

public class OuterOne                             // enclosing class
{
  private void calculate() {   }
  class InnerOne		                  // enclosed class or inner class
  {
    public InnerOne()
    {
      calculate();
    } 
  }
  public void outerMethod()
  {
    InnerOne io = new  InnerOne();
  }
  public static void main(String args[])
  {
    OuterOne oo = new OuterOne();
    oo.outerMethod();
  }
}

InnerOne is enclosed within OuterOne. calculate() method is defined within OuterOne. Because the Java Virtual Machine has restrictions on calling private members from outside of their class, a special access method is generated by the compiler and added internally to the OuterOne class. This method has a name access$0(), and it in turns calls calculate(). In other words, a method is generated that can be called from outside of InnerOne, and grant access to a private method of OuterOne. So when calculate() is called, the above generated method access$0() is called, and it in turn calls calculate(). So use inner classes, that too access the private data of the outer, only when the code demands.

11. String and StringBuffer

String is designed to be immutable. A string is a sequence of Unicode characters and represented by an object of String class(no terminating \0). Immutable means once created, they never change. If you reassign a new value, the new value is stored in a new location and the new location reference is stored in the String object and the old value is garbage collected. This increases a lot of overhead and should be avoided. If a string value is to be changed, the programmer should prefer StringBuffer which is mutable.

String str = "Hello";
str = str + "World";

the string "Hello", once created, does not change, but a reference to the string (value) may change. The string reference in str originally points to "Hello", but then is changed to point to a new string formed by concatenating str and "World". The above sequence is implemented internally using code like:

String str = "Hello";
StringBuffer sb = new StringBuffer(str);
sb.append("World");
str = sb.toString();

In other words, the two strings to be concatenated are copied to a temporary string buffer, then copied back. As it is clear, such concatenation a big overhead. We should not use + and += with strings.

public class StringOverhead 
{
  public static void main(String args[]) 
  {                                           // using +
    Clock c1 = new Clock();
    String s1 = "Hello";
    for (int i = 0; i < 10000; i++)
        s1 = s1 + "World";
    c1.printTime("Concatenation with +");

                              // using StringBuffer
    c1.setZero();
    String s2 = "Hello";
    StringBuffer sb = new StringBuffer(s2);
    for (int i = 0; i < 10000; i++)
      sb.append("World");
    String s3 = sb.toString();
    c1.printTime("Concatenation with StringBuffer");
  }
}

String concatenation, nearly 2500 times, makes the process to run slower than using StringBuffer.

11.1 Accumulating data Using char[] Arrays

To concatenate characters, there is a better and effective way than using a StringBuffer. We place the data in a character array and then converting the array into a string.

public class CharArrayDemo
{
  public static void main(String args[])
  {
    char ch[] = new char[10000];
    int counter = 0;
    Clock c1 = new Clock();
    for(int i = 0; i < 10000; i++)
    {
      ch[counter++] = 'A';
    }
    String s1 = new String(ch, 0, ch.length);     // SOP(s1) takes a long time
    c1.printTime("Time");
  }
}

The time taken for the above is very negligible and most probably below 1 millisecond.

11.2 Using == and equals() with Strings

The logical operator, in case of strings, compare whether both the string objects refer to the same location or not whereas equals() method compares whether both strings contain the same characters or not, but not their locations. It is a little bit confusing concept for a novice.

Using == is less expensive (overhead) than using equals() method. To increase the performance, it is better to check first with == and then equals(). If == turns to true, then equals() method is short-circuited(not executed) which sometimes works out.

	if (s1 == s2 || s1.equals(s2))
        {
	    // some code
	}

11.3 Using the length() of a String

Using length() method of a String once or while in a program is not a big botheration. But used in a loop, a number times, may decrease the performance.

public class StringLength
{
  public static void main(String args[])
  {                                        // create a big string first                                  
    StringBuffer sb = new StringBuffer();
    for(int i = 0; i < 1000000; i++)
    {
      sb.append('@');
    }
    String s1 = sb.toString();      // convert the StringBuffer into a String

                                    // using length() method
    int counter = 0;
    Clock c1 = new Clock();
    for(int i = 0; i < s1.length(); i++)
    {
      if(s1.charAt(i) == 'S')
	    counter++;
    }
    c1.printTime("Using length() method");

                                   // using a value equivalent to length()
    counter = 0;
    c1.setZero();
    int len = s1.length();
    for(int i = 0; i < len; i++)
    {
      if(s1.charAt(i) == 'S')
	counter++;
    }
    c1.printTime("Using an integer equivalent to length");
  }
}

Using length() method is more expensive than using a precomputed value. Avoid using length() in a tight loop.

11.4 Using toCharArray()

charAt() is a String method similar to length(), which can be expensive if called for every character in a long string. An alternative to this method is use of toCharArray(), which returns a copy of the string in a char[] array fashion.

public class CharAt
{
  public static void main(String args[])
  {	                                        // create a big string first                                  
    StringBuffer sb = new StringBuffer();
    for(int i = 0; i < 1000000; i++)
    {
      sb.append('@');
    }
    String s1 = sb.toString();                  // convert the StringBuffer into a String
				                // using charAt()
    int counter = 0;
    int len = s1.length();
    Clock c1 = new Clock();
    for(int i = 0;  i < len; i++) 
    {
      if (s1.charAt(i) == 'x')
         counter++;
    }
    c1.printTime("Using charAt()");
	                                         // using toCharArray()
    counter = 0;
    char ch[] = s1.toCharArray();   // convert the string into a character array
    int len1 = ch.length;
    c1.setZero();
    for (int i = 0; i < len1; i++) 
    {
      if (ch[i] == 'x')
	counter++;
    }
    c1.printTime("using toCharArray()");
  }
}

In other words, converting the characters of a string into a char[] array eliminates the method call overhead of charAt(). But char[] array takes more space. Moreover, length() method of String is more expensive than length variable of an array. First uses a method call and the later uses a runtime descriptor.

11.5 Converting strings and double to Wrapper objects

Suppose you have a string like "35.19" that represents a number, and you'd like to convert it to numeric form. How expensive is such an operation? One way to find out is by writing a small program that creates a Double (a wrapper class for double), from either a string or a number.

public class StringsToNumbers
{
  public static void main(String args[])
  {
    String s1 = "35.19";
    double d1 = 35.19;
    Double d;
            		                       // constructing a Double value from a string
    Clock c1 = new Clock();
    for(int i = 0; i < 100000; i++)
    {
      d = new Double(s1);
    }
    c1.printTime("Converting string to Double");

	                                   // constructing a Double value from a double value
    c1.setZero();
    for(int i = 0; i < 100000; i++)
    {
      d = new Double(d1);
    }
    c1.printTime("Converting double to Double");
  }
}

Constructing a Double from a string value is 60 times expensive to a double value.

5 thoughts on “Java Performance Tips 1”

Leave a Comment

Your email address will not be published.