Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4524848
Votes 0
Synopsis StringBuffer's setLength() Results in Unneeded GC
Category java:classes_lang
Reported Against merlin-beta3
Release Fixed 1.4.1(hopper)
State 10-Fix Delivered, Verified, bug
Priority: 4-Low
Related Bugs 4724129
Submit Date 08-NOV-2001
Description




java version "1.4.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-beta3-b84)
Java HotSpot(TM) Client VM (build 1.4.0-beta3-b84, mixed mode)


It's a very common Java idiom to append text to a StringBuffer and then return
the corresponding String using the StringBuffer's "toString()" method. When
using a StringBuffer it is common (and  customer  practice) to set the length of the
StringBuffer ahead of time to prevent the StringBuffer from continuously
allocating progressively larger char arrays which must then be GCed.

If you do *not* pre-set the length of a StringBuffer you end up with a default
length of 16 characters.

Here's the problem: If you create a String from a StringBuffer and then you
later set the length of the StringBuffer to zero, the internal size of the
StringBuffer's char[] shrinks down to the default 16 characters, essentially
undoing the capacity setting. Here's an example:


public class Tester {
   static StringBuffer sb = new StringBuffer(1024);

   public static void main(String[] args) {
      System.out.println("length:" + sb.length() +
                         ", capacity:" + sb.capacity());
      buildSomeString();
      System.out.println("length:" + sb.length() +
                         ", capacity:" + sb.capacity());
      buildSomeString(); // capacity shrinks to 16
      System.out.println("length:" + sb.length() +
                         ", capacity:" + sb.capacity());
   }

   static String buildSomeString() {
      sb.setLength(0); // clear out old characters
      System.out.println("after setLength length:" + sb.length() +
                         ", capacity:" + sb.capacity());
      sb.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
      return sb.toString();
      // return sb.substring(0, sb.length()); // problem goes away...
   }
}


If you run this code you will see that after "buildSomeString()" is called the
second time, the capacity of the StringBuffer has been reduced to 16. This
problem only occurs if a String constructor has been passed a StringBuffer
 customer  as a parameter (which causes the StringBuffer's "shared" flag to be set
to true) *AND* the StringBuffer's length is later set to zero. The coding error
is in the "StringBuffer.setLength()" method.

If you change the return statement in the "buildSomeString()" method to "return
sb.substring(0, sb.length());", the problem goes away.

When a programmer sets the capacity of a StringBuffer, he does so to increase
efficiency. The "setLength()" method sneakily undoes the programmer's  customer 
intentions by setting the capacity back to the default of 16 characters, for no
 customer  reason.
(Review ID: 135229) 
======================================================================
Work Around




Customer Workaround :
Instead of calling StringBuffer.toString(), call StringBuffer.substring(0,
sb.length());
======================================================================
Evaluation
We will remove the special case for setLength(0) in Hopper to keep this
from happening and use some other approach to prevent 4259569 from recurring.
  xxxxx@xxxxx   2001-12-04

The problem described in 4259569 can be fixed in Tiger by allowing compaction of a StringBuffer if someone wants to be more memory efficient at the expense of performance. This will occur under bug number 4546734.
  xxxxx@xxxxx   2001-12-04
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang