JDK-4259569 : StringBuffer.toString() can cause large memory usage
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.2.0
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 1999-08-04
  • Updated: 1999-10-26
  • Resolved: 1999-10-26
Related Reports
Relates :  
Description

Name: rlT66838			Date: 08/04/99


java -version
java version "1.2"
Classic VM (build JDK-1.2-V, native threads)

java -fullversion
java full version JDK-1.2-V

The problem observed is that the toString() method of
StringBuffer will return a String object which shares the
storage of of the StringBuffer. Subsequent modification of
the StringBuffer causes a copy of the storage. In situations
where the StringBuffer is reused in this fashion, once the
buffer grows to a large size (or is initialized to a specific
size using the StringBuffer(int) constructor) then each String
object created by toString will have a storage size the same
as the (large) StringBuffer size even though very little of
that storage is used by the String. Therefore the practice of
reusing StringBuffer objects (which is a good practice since
the alternative increases object creation/deletion as well
as GC times) is a detriment to memory usage anytime the
StringBuffer's storage grows large compared to some of the
String's that might be created from it.

I believe this shareing behavior of toString goes against the
implicit logistics associated with StringBuffer objects and
can cause very bad problems if StringBuffer's are reused.
The current documentation contains a vague "implementation note"
mentioning the storage sharing without emphasizing the inherent
problem in this. MINIMALLY the documentation should stress what
bad behavior can result. Ideally I think toString should not have
this behavior as it is unexpected and counter-intuitive.
Achieving a storage sharing between String and StringBuffer
object would probably be better served in other ways:

1) a different method other than toString() can be called.
2) add a method 'boolean setShareStorageAllowed(boolean)' could
be added to control toString's behavior.

===Test Case=========
//Test program: bug.java

public class bug {
    // Allocate a large (1 Meg) static buffer that will be reused.
    static StringBuffer buffer = new StringBuffer(1024 * 1024);

    public static void main(String[] args) {
 final int limit = 100;
 int i;
 String[] s = new String[limit];

 for (i=0; i < limit; i++) {
     // Clear buffer then add to it
     buffer.setLength(0);
     buffer.append(" ");

     // Create a string from the buffer and save it.
     s[i] = buffer.toString();

     // Report progress
     System.out.println("Created String " + i);
 }
    }
}

Produces the following output:
Created String 0
Created String 1
Created String 2
Created String 3
Created String 4
Created String 5
Created String 6
Created String 7
Created String 8
Created String 9
Created String 10
Created String 11
Created String 12
Created String 13
Created String 14
Created String 15
Created String 16
Created String 17
Created String 18
Created String 19
Created String 20
Created String 21
Created String 22
Created String 23
Created String 24
java.lang.OutOfMemoryError
 at java.lang.StringBuffer.setLength(Compiled Code)
 at bug.main(Compiled Code)
Exception in thread "main"

Due to the unexpected semantics of StringBuffer.toString we have created 24
String objects which each point to 1 Meg of storage even though only a
single character of each is actually used by the String.

Let me know if I can be of further help in describing the problem.
(Review ID: 88266) 
======================================================================

Comments
WORK AROUND Name: rlT66838 Date: 08/04/99 Replace calls such as: buffer.toString() with buffer.substring(0, buffer.length()) ======================================================================
11-06-2004

EVALUATION Reuse of StringBuffers is not recommended, especially now that Hotspot is available. If you feel that you must reuse a StringBuffer, the setLength(0) call now sets the StringBuffer storage back to the default size. michael.mccloskey@eng 1999-10-25
25-10-1999