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: 4724129
Votes 520
Synopsis Memory leak in use of StringBuffer.toString()
Category java:classes_lang
Reported Against 1.4.1 , hopper-rc , mantis-beta
Release Fixed 1.4.1_05, 1.4.2(mantis-b21) (Bug ID:2057021)
State 10-Fix Delivered, Verified, bug
Priority: 2-High
Related Bugs 4259569 , 4524848 , 4546734 , 4910256
Submit Date 31-JUL-2002
Description
  xxxxx@xxxxx   2002-07-31

J2SE Version (please include all output from java -version flag):
java version "1.4.1-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-
Java HotSpot(TM) Client VM (build 1.4.1-rc-b18, mixed mode

Does this problem occur on J2SE 1.3 or 1.4?  Yes / No (pick one)
 No. 1.4 only reports <2MB heap, 
     1.4.1 reports > 13MB heap.
  
Operating System Configuration Information (be specific):
  Win2K SP2
  (Same problem can be reproduced on Solaris 8:
   1.4 -
     Got 174 properties
     Used 3520K
   1.4.1 -
     Got 174 properties
     Used 17512K <----- ~5 times

Hardware Configuration Information (be specific):
  dual 866Mhz Pentium IIIs, 1Gb RAM.

Bug Description:
  Memory leak problem when use Hopper build 18. The application itself 
  basically parses an XML file and has some reflection based code to turn 
  the XML into method calls on an data  customer .

Steps to Reproduce (be specific):
  1. unjar testcase.jar file
  2. javac *.java
  3. java JDK141Leak

  JDK1.4.1 the app reports it uses > 13Mb heap,
  running same app under 1.4 reports <2Mb heap.

In real life the non-stripped down version parses a bunch of XML 
messages using the same technique. Under 1.4.1 it runs out of memory 
with a 256Mb heap whereas in 1.4 it doesnt even reach 64mb heap size.

  

  xxxxx@xxxxx   2002-08-20

More information from the customer:

After further investigation it has been narrowed the problem down to a
call java.lang.StringBuffer.toString().

However, this call has to take place while parsing an XML file, if  
simulate the XML parsing by calling the appropriate methods by hand there 
is no leak.

here is the algorithm used as follow:

Use SAX to parse XML file
   If SAX gets a new element, call startElement() method and append XML
      element name onto XPATH
   If SAX gets new characters, call text() and append the characters into 
      a StringBuffer
   If SAX gets an end element, call endElement() and use the current XPATH 
      to lookup a command pattern and pass it the contents of the current
      StringBuffer. Then remove element name from XPATH.

Its the passing of the contents of the current StringBuffer that is 
causing the leak. Look for the PROBLEM comment in PropertyResponseParser.java

New stripped down code is in the attached jar(JDK141LeakV2.jar).




DESCRIPTION OF THE PROBLEM :
Refer to bug ID (4724129)
There is a very significant memory leak in the StringBuffer class. I ran my application using -verbose:gc under j2sdk1.4.0 and under j2sdk1.4.1rc and the memory usage went up dramatically in j2sdk1.4.1rc.

I researched the problem in the bug database and saw something about the bug id referring to StringBuffer class and as a test I replace in the j2sdk1.4.1rc rt.jar file the StringBuffer.class from j2sdk1.4.0 class and it went back to normal.

I did a difference on the two classes and the problem appears in the setLength method where the following code:
	if (count < newLength) {
	    if (shared) copy();
	    for (; count < newLength; count++) {
		value[count] = '\0';
	    }
	} else {
            count = newLength;
            if (shared) {
                if (newLength > 0) {
                    copy();
                } else {
                    // If newLength is zero, assume the StringBuffer is being
                    // stripped for reuse; Make new buffer of default size
                    value = new char[16];
                    shared = false;
                }
            }
        }

was replaced in the 1.4.1rc version with:
	if (count < newLength) {
	    if (shared) copy();
	    for (; count < newLength; count++) {
		value[count] = '\0';
	    }
	} else {
            count = newLength;
            if (shared) copy();
        }

I think this is an IMMEDIATE must fix bug! It would be simple to reverse to the previous code for StringBuffer to solve this simple but huge problem!

(Review ID: 163802)
======================================================================
Work Around
Don't reuse StringBuffers. If you do reuse them, check their capacity and make sure that you aren't making tiny Strings from huge StringBuffers.
  xxxxx@xxxxx   2002-08-22
Evaluation
From Jane,
I just duplicated what you saw with 1.4.1 VM and 1.4.0 libraries
(finally set myself up with that combo):

 isher 64 =>java -XXaltjvm=c1-1.4.1 -version
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.1-rc-b19, mixed mode)

 isher 65 =>java -XXaltjvm=c1-1.4.1 -verbose:gc JDK141Leak
about to start parsing...
[GC 2047K->338K(3520K), 0.0285096 secs]
[GC 2386K->391K(3520K), 0.0071823 secs]
[GC 2439K->433K(3520K), 0.0052143 secs]
[GC 2481K->488K(3520K), 0.0036402 secs]
Got 174 properties
Used 3520K


So I'd suspect the libraries off the top of my head:
  1.4.1 libraries + 1.4.1 VM     bad behavior
  1.4.0 libraries + 1.4.0 VM     good
  1.4.0 libraries + 1.4.1 VM     good

==============================================================
No parser changes were made between JDK 1.4.0 and 1.4.1 so it cannot be
a problem with the parser code, ie. JAXP.  Also, I tried to understand what the code does.  Can you provide a simpler test case that parses an XML file?

For help on using JAXP, see http://xml.apache.org/~edwingo/jaxp-faq.html.  Ultimately, this bug needs to be filed under a different category.

  xxxxx@xxxxx   2002-08-14
========================================================
Submitter provided more info which points to StringBuffer class.
  xxxxx@xxxxx   2002-08-21
=========================================================


This is a result of the decision to remove the fix for 4224987
in Hopper, which was done under bug number 4524848.
  xxxxx@xxxxx   2002-08-22

The old fix cannot be reinstated because it changes the backing array size in circumstances in which it shouldn't (as well as sometimes when it is desired). Other fixes have been considered but rejected because of performance impact. This will be fixed in Tiger by the removal of String/StringBuffer sharing.
  xxxxx@xxxxx   2002-12-05

We have restored the 1.3-1.4 setLength(0) behavior in Mantis as a bridge to the more correct fixes available in Tiger.
  xxxxx@xxxxx   2003-04-17
Comments
  
  Include a link with my name & email   

Submitted On 11-OCT-2002
condass
We are having the same problem when using JDOM in the 
1.4.1 environment.  We process huge XML documents on our 
servers and have found that we exceed the maximum physical 
memory on our servers when using 1.4.1.  Using -Xrunhprof I 
can see that there is a leak associated with the StringBuffer 
class.  We will be staying on 1.3.1 until the problem is 
resolved.


Submitted On 14-NOV-2002
davey
With the old VM:

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

Our server takes 6 MB when it comes up
(Runtime.totalMemory()-Runtime.freeMemory()).
After parsing an XML doc w/ JDM it's up to 20MB via
the same calculation.

With the new VM:
c:\dev\v52pb>java -version
java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build
1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed 
mode)
The process takes up 24M to start and to parse the
XML doc takes ...forever..and uses approx 250MB.
Amazing huh?





Submitted On 15-NOV-2002
corby
We use JDOM and got completely hosed by this bug. We are 
backing JDK 1.4.1 out of our production environment now.


Submitted On 15-NOV-2002
ericburke
I'm pretty sure this leak is causing problems for us. We must 
restart our app server every single night now that we are on 
Java 1.4.1. We had to increase our heap size for all of our 
client apps, as well, and still run out of memory after about a 
day of usage. When will this be fixed?


Submitted On 18-NOV-2002
osbald
JDOM suffers horribly from this bug, which isn’t good news for 
1.4.1 as its used widely behind the sense in many of the more 
popular third-party java apps, including a couple of mine. 
Development on JDOM seems to have slowed significantly 
(due to the developers workloads, fame & fortune - rock & roll 
lifestyle etc..) but there is a patched version of  JDOM beta9 
which can be checked out from the JDOM CVS repository – 
one of the enhancements is a patch specifically for this bug.. 
I’ve been using beta9 for just over a month without any 
significant problems, and no more spurious  “out of memory 
exceptions” from 1.4.1..


Submitted On 05-DEC-2002
bestsss
Workaround:... [ broken code will not be fixed, though :( ]
use StringBuffer.substring(0) instead of toString()
when sharing StringBuffer.
the method allocated exactly as needed bytes for the
returned String.


Submitted On 27-DEC-2002
DouglasBlair
I needed a PrintStream with UTF-8 output encoding to
generate flatfiles from my XML, so I needed 1.4.x.  The
workaround with substring() helped.  This is a serious bug
and ought to be fixed in 1.4.1_02.  My .02, anyways...


Submitted On 09-JAN-2003
tupari
Why are you wating for Tiger???? That won't be out until the
end of 2003!  It is simple to make the toString() the
equivalent of substring(0)


Submitted On 13-JAN-2003
dserodio
I agree this is a serious showstopper for 1.4.1. Waiting
until Tiger to fix it will make 1.4 simply useless for lots
of people (myself included).


Submitted On 23-JAN-2003
aanecito
Seems as if this would prevent usage of the JVM sun may be 
thinking of using for the Windows release that was/is to occur 
in the next 120 days? I intend not to recommend the upgrade 
to my company and I am a big java supporter. This scares me 
a lot!


Submitted On 27-JAN-2003
bestsss
the workaround: substring(0) is NOT replacer of toString().
this will result of huge drawback, because creating every
String would require double memory/room (and much more work
of garbage collector).
Actually the vast majory of programs dont share StringBuffer
(or they do it w/ same length Strings), so they are not
affected by the bug.

Besides, I never use StringBuffer w/ sharing. This always
results (even w/o the bug) in slower operation due to sync.
A class called com.xxx.util.AsynchStringBuffer is a
replacer. Since Strings are always copied (when shared)
there is no using utilizing a java.lang.StringBuffer in such
cases.


Submitted On 31-JAN-2003
ChristianHujer
I think this is an immediate must fix!
Using JDK 1.4.1, the default heap size is already not enough
when simply invoking ant to transform 15-20 XML files using
Xalan and Xerces. With JDK 1.4.0 there are no memory problems.
I had to increase my heap size to 256 MB to get medium
transformations working. Larger transformations require 512
to 1024 MB heap size.
This simply *must not* be.


Submitted On 10-FEB-2003
tahasam
Why is this not being fixed in Mantis (1.4.2)? Mantis will be 
out early this year. This must NOTwait till Tiger (1.5). Tiger is 
a year away!


Submitted On 11-FEB-2003
passwordp
Gafter, I really don't care what the spec. says. 
StringBuffer.toString() is a special case of 
StringBuffer.substring(x), where x = 0. Consequently, the 
effects of StringBuffer.substring(0) and StringBuffer.toString() 
should be identical.  I would therefore argue that the spec. is 
inconsistent. As the spec. is inconsistent, fix the code now, 
and the spec later.

Also, why is this fix too big for a maintenance release? The 
bug was introduced in a maintenance release - so why not 
remove it in a maintenance release?!


Submitted On 11-FEB-2003
passwordp
WHAT ARE YOU GUYS - MORONS??? This is so incredibly easy 
to fix (calling it a quick win would be an understatement) - 
release it in 1.4.2 for Christs sake!


Submitted On 11-FEB-2003
mangri
This is a very annoying bug since every serious application 
using jdom does not work on 1.4.1.

Regarding jdom a simple workaround in the jdom-b8 source 
can help you out: 
Update flushCharacters in org.jdom.input.SaxHandler.java - 
see the "// mg" - "// gm" tagged section:
---------------------------------------------
    protected void flushCharacters() throws SAXException {

        if (textBuffer.length() == 0) {
            previousCDATA = inCDATA;
            return;
        }

        /*
         * Note: When we stop supporting JDK1.1, use 
substring instead
        String data = textBuffer.substring(0);
         */
        // mg: Because of java 1.4.1
        // String data = textBuffer.toString();
        String data = textBuffer.substring(0);
        // gm
        textBuffer.setLength(0);
---------------------------------------------


Submitted On 11-FEB-2003
gafter
The fix is only "easy" if you're not the one that has to do it.
The fact of the matter is that the old code didn't satisfy
the specification for StringBuffer because toString() changed
the capacity(), which it is not allowed to do.  The "easy" fix
is to always copy the characters from the StringBuffer in
toString(), but that has a significant performance impact
on all clients.  The current implementation is correct while the
old one wasn't.  The best fix will be to rework the way
strings and
string buffers work together, or to change the spec for
toString()
so it is allowed to change the capacity, but either is a
larger change
than should go into a maintenance release.

If this problem affects your code, then rewrite your code to
stop
reusing StringBuffers.


Submitted On 14-FEB-2003
Parkway
// If newLength is zero, assume the StringBuffer is being
// stripped for reuse; Make new buffer of default size
value = new char[16];
shared = false;

Perhaps you should keep track of the last requested
capacity. Or at least document the pros and cons of
setLength(0), so that people don't use it inappropriately.


Submitted On 24-FEB-2003
wmshub
Actually, there does seem to be as easy fix that should make
everybody happy; the requirements seem to be:

- toString() should not change string buffer capacity. Until
1.4.1 it did.
- toString() should not always copy the buffer, which would
hammer the GC badly on a lot of applications. It has never
done this.
- Fix the *@!$# memory leak introduced in 1.4.1.

Note that the memory leak only shows up when you have a
high-capacity short-length string buffer and you call
toString(). My solution? Look at this:

public void toString() {
  if (length() * 2 <= capacity()) {
    sharedToString();
  } else {
    copyingToString();
  }
}

See the point? If the string buffer is huge, and the string
is small, copy it. If the capacity and length are close,
share it. That way you don't make needless copies and fill
up memory with temporary objects, but you also don't fill up
memory with huge character arrays when your strings only use
the first few characters!

What is so hard about this, that it can't get into Mantis?
Am I being blind here, and there is some reason my easy fix
won't work?


Submitted On 11-MAR-2003
werezak
I wonder if Sun is reusing StringBuffers in the -Xrunprof 
feature.  My properly working application goes "out of 
memory" if I use this option.


Submitted On 17-MAR-2003
jacyg
This has to be one of the most ridiculous bugs I've ever
seen.  With XML so prevalent, and most people using tools
and utilities that they don't have direct control over, an
admonishment to "don't reuse StringBuffers" is almost
arrogantly insulting.  Does Sun have some sort of desire for
people to not use 1.4.1+ for real world applications??? 
Real world applications make use of libraries written by
other people--it's asinine to expect that people are going
to entirely reengineer their applications to work around
this bug, especially since it didn't exist before (yes, it's
lovely that you fixed the *other* bug that existed, bravo,
but let's give some thought to relative severity here, shall
we??).


Submitted On 18-MAR-2003
PeBecker
We had an example where loading a 500kB XML file using
JDOM's SAXBuilder needed 500MB+ of heap size, which is a
no-go for deployment of the app. Current workaround is using
the DOMBuilder instead, but that is not a satisfying
solution. Not supporting 1.4.1 isn't satisfying either, we
are talking about an out-of-the-box product and we strongly
dislike playing these "don't use this JDK, don't use that
JDK" games. Customers blame use for bad support if that happens.


Submitted On 24-MAR-2003
bobHein
This really needs to be fixed by 1.4.2 at the latest; it is 
preventing us from updating from 1.4.0 to 1.4.1!!


Submitted On 31-MAR-2003
synkronix
Could this perhaps be addressed in an alternate manner?
Removing sharing seems like a bad idea.  Replacing the 
backing array seems like a bad idea.

Just an idea.  How about supporting array size modification at 
the array object, adding a setLength(int) method to the array 
object?  (This may indeed require magic by the JVM; perhaps 
it would only support decreasing the size, or resizing within 
the original allocation size, depending on how it's implemented 
at the lower level.)

In such a case, setLength(int) would be supported directly on 
the backing array in StringBuffer (and many other locations).  
The object reference would remain intact.

This is an operation I have to do on my own arrays quite 
often, so having this supported as a method on the array 
object itself would be handy in general.  (A fill method on 
arrays would also be quite useful.)  Who said length() could 
be the only method ever defined for arrays...


Submitted On 02-APR-2003
jeffcampbell
Because of this issue, we have been getting a lot of out of
memory errors in our program.  To solve this, we are forced
to distribute our software with the 1.4.0 version of java
instead of the 1.4.1 version.

FYI  We are using JDOM, which uses the StringBuffer object


Submitted On 03-APR-2003
thib_gc
I voted for this bug some time ago, because I read compelling 
arguments from all of you here (thanks for sharing). Today, 
this bug hit me where I was expecting it the least. The 
following snippet of code caused a StackOverflowError:

// Start
ClassLoader loader = this.getClass().getClassLoader();
contentPane.add(new JLabel(new ImageIcon
(loader.getResource("images/my_icon.png"))), 
BorderLayout.CENTER);
// End

And why? Well, apparently, the intermediate URL generated 
by the class loader gets parsed somewhere in 
java.net.URLStreamHandler which then calls toString in 
java.lang.StringBuffer. This is sad! What are we going to do if 
we can't even open files properly? :-)

Please fix this bug as soon as possible.


Submitted On 08-APR-2003
kre
StringBuffer looks like one of the first victims to be hit
with the refactoring stick. For instance, encapsulating all
uses of System.arraycopy and decide about sharing there can
even save some copy operations. Because you know already
what area will be overwritten now, exclude it from the
copying if it looks worthwhile.

There is one case where I would like to see sharing in the
future: when the value.length == size. This is for code that
precomputes the resulting length, and I would expect most
such code benefit from the sharing and unlikely to reuse the
StringBuffer.


Submitted On 22-APR-2003
manu4ever
Why can't this fix be backported to 1.4.1_03?


Submitted On 23-APR-2003
johanges
Not so easy. In Real Life you can't just go and fiddle with people's JVM installations like that.  I don't have the rights to redestribute a modified JVM to my customers (and I can't properly sign the jar anyway.)

It really should be in 1.4.1_03 to be of any use.  The description "Closed, Fixed" is a bit missleading.  Maybe a "Closed, Fixed, To be shipped in n.n.n-nn" would be of more value.


Submitted On 23-APR-2003
jacyg
if you really want it backported, it's not so hard to do it
yourself....just replace the StringBuffer.class in 1.4.1x
with the version in 1.4.0


Submitted On 27-APR-2003
manu4ever
Apparently this bug is fixed in 1.4.1_04, but the 1.4.2 FCS will 
almost certainly be out before that.

Let's hope that the first release of 1.4.2 is a bit more quality-
focussed than the first release of 1.4.1.


Submitted On 09-MAY-2003
dserodio
It's fixed in 1.4.1_04, but where can I download it? The
download page only has 1.4.1_02!!!


Submitted On 13-MAY-2003
dserodio
Where can I download this testcase.jar? I'd like to run some
tests on my own...


Submitted On 19-MAY-2003
sharpt
In my opinion, StringBuffer shouldn't be final, which means that it's impossible to subclass it
to correct a problem like this. I have created an alternate implementation, named
ReuseStringBuffer, that avoids the sharing problem, but it cannot be passed to methods that
expect a StringBuffer. Please, when you fix the sharing problem, also remove the final from
StringBuffer. My implementation of setLength does no copy (copy occurs for the toString):
  public synchronized void setLength(int newLength) {
    if (newLength < 0) {
      throw new StringIndexOutOfBoundsException(newLength);
    }
    if (newLength > value.length) {
      expandCapacity(newLength);
    }
    count = newLength;
  }
The boolean <code>shared</code> is needed for StringBuffer
because the String constructor collaborates with StringBuffer to avoid copying
the array unless it is reused after a toString().
Because of this feature, ReuseStringBuffer.setLength(0) reuses the existing value array.
This saves ReuseStringBuffer from having to extend a new array of default capacity
and also avoids assigning unused buffer to the new String created by the toString().


Submitted On 31-MAY-2003
passwordp
Nice to know common sense finally won out.


Submitted On 16-JUN-2003
pigasus1
The fix is listed for 1.4.1_04, but I don't see that build
anywhere. Is it forthcoming? Is it hiding? I too need this fix.


Submitted On 18-JUN-2003
voorde
Instead of replacing the StringBuffer.class in rt.jar would it 
work to add an extra jar containing only the 1.4.0 StringBuffer
in the endorsed directory?


Submitted On 23-JUN-2003
gjacobi
When will 1.4.1_04 be available?  Like everyone else posting
here, we need this BAD.


Submitted On 24-JUN-2003
mdunstan
We use xml-rpc 1.1 from apache. The following fix worked for
our app.
The solution was posted here:
http://www.mail-archive.com/rpc-dev@xml.apache.org/msg00707.html

Here is the solution we applied:

> I fixed it (somewhat aggressively) by changing the line in
> XmlRpc.java that reads:
>
>               parser.parse (new InputSource (is));
> to:
>               try
>               {
>                       parser.parse (new InputSource (is));
>               }
>               finally
>               {
>                       cdata = null;
>               }

It also appears JDOM beta 9 includes a fix for the memory leak.
Here are links to the jar files for xmlrpc-1.1 if you don't
want to build it.

www.luxorindustries.com/xmlrpcpatch/xmlrpc-1.1.jar
www.luxorindustries.com/xmlrpcpatch/xmlrpc-1.1-applet.jar


Submitted On 30-JUN-2003
kosmikov
This is marked as fixed in "mantis-rc", so now that 1.4.2
has been released, why isn't it in the list of fixed bugs?
(http://java.sun.com/j2se/1.4.2/fixedbugs/fixedbugs.html). 

Surely it qualifies for entry in that list, with having 520
votes! 


Submitted On 11-JUL-2003
jgh147
Where is 1.4.1_05 ? I can only see 1.4.1_03 on this site.


Submitted On 23-AUG-2003
atrajano
Has this been fixed on 1.4.2 yet?


Submitted On 19-NOV-2003
OKLeslie2
I'm still having a problem with StringBuffer in version/build 
1.4.2-b28 - I submitted a bug explaining this on Nov 18 2003


Submitted On 07-SEP-2006
durai.at.sun
Fair



PLEASE NOTE: JDK6 is formerly known as Project Mustang