[This tip was excerpted from the new Wrox Press book Oracle 9i Java Programming.]
Performance optimization is a big topic; big enough to fill an entire book of information on how to make Oracle and Java perform better. In this and subsequent tips, we will look at just a few ways to improve the performance of your Java programs.
Since String objects are immutable, the creators of Java included the StringBuffer class in the core Java API. The StringBuffer class is a class that acts as a buffer for the creation and manipulation of dynamic strings. In fact, even if you don't explicitly use the StringBuffer, the compiler uses it for you whenever you concatenate string references. If you have source code like this:
A new String is created and the reference is assigned to the variable html. The Java compiler creates the new String by treating the source code as if you had coded this:
The StringBuffer(String) constructor creates a StringBuffer that is sized to the same number of characters as are contained in the string argument. As soon as append() is called, the StringBuffer object must resize its internal buffer to hold additional characters. Due to the additional method calls, additional buffer objects, and additional memory allocation, string concatenation tends to be very inefficient. In the next example, we'll see how to use the StringBuffer to dramatically improve the performance of Java code that dynamically creates strings.
By taking control of the StringBuffer creation, we can eliminate all the waste caused by string concatenation and caused by allowing the compiler to create StringBuffer method calls. The StringBuffer has a constructor that takes an integer parameter:
This parameter instructs the StringBuffer object to initialize its internal buffer to be the specified number ...
To continue reading for free, register below or login
To read more you must become a member of SearchOracle.com
');
// -->

of characters. As long as we do not append more than that many characters to the StringBuffer, there will be no additional method calls to resize the buffer, and no reallocation of memory. The performance improvement is impressive, as we will see in the StringDemo2 class.
Modify the StringDemo1 class as shown below to create the StringDemo2 class:
When the Java class is successfully compiled, create a call specification:
Use the following PL/SQL statements to set the environment so that output is directed
to the console:
Now, call the procedure:
As I said, the improvement is dramatic, from 132 seconds (131860 milliseconds) to 3
seconds (2704 milliseconds). That's more than a 40X improvement. No matter how easy it
is to do string concatenation:
Avoid the temptation! As we saw in the example above, sometimes a little work on the part of
the programmer can yield huge performance improvements.
In this case, we created a
StringBuffer object that was large enough to hold the string we wanted to create. As we
append to the StringBuffer, the object never needs to resize its buffer, so all the expensive
memory reallocations and object creation that occurs with string concatenation is eliminated.
Owing to this dramatic difference in execution times, it makes sense to keep this in mind
whenever you are concatenating String references in Java. If the method is going to be called
rarely, you can choose to use the '+' operator to concatenate strings. If however, the method
will be called a lot, you will definitely want to consider using a StringBuffer in the code.
To
do this in your own code, you'll need to estimate how large the string is that you want to
create could possibly be. In the example above it was easy because I somewhat arbitrarily sized
the StringBuffer to 300,200 characters without worrying about how many rows would be returned. I
chose this value because I knew, from runs of the previous example, that this size would be
large enough to hold the string that was created by the code. In your own code, you'll need to
look at how many rows you want to display to the user, and how large each row could be. You can
test that estimate by printing out the size of each completed StringBuffer.
If you're getting
strings from some source other than a table, it will be harder to make the estimate, but even a
rough estimate will be better than starting with the default buffer size. When you use the
constructor:
The default size is 16 characters. Every time
the buffer resizes, it adds 1 to the current size and then doubles it. So the progression of
buffer sizes is 16, 34, 70, 142, 286, 574, 1150, and so on. Suppose the actual string size is
going to be 1000 characters, but your rough estimate comes out to 500. Even with your estimate
being 50% too low, you've still eliminated 5 calls to the resize method.
Keep in mind that using
StringBuffers is not a cure all. You can still consume all the memory in the system and cause
the program to crash by appending enough characters to the StringBuffer. In the application I
worked on, our second step in the solution was to page all queries. When a query returned rows,
we only showed a certain number (it was a user configurable parameter) to the user. If the
number of rows returned was more than the number displayed, we gave the user a Next button to
get the next set of rows from the query. Using these techniques, we eliminated all memory
problems from the application.