xarax said:
That is total baloney. Any JDK that causes a memory leak
with StringBuffer is broken.
Well it isn't total baloney. There are some things you can do that can cause
you to suck up a great deal of memory. Unfortunately, StringBuffer in the
Kaffe implementation and in JDK1.4.1 were broken (see below).
Alan is right that it is best to not use a StringBuffer anymore after you
call toString on them. You can do it, but there can be problems with excess
memory being used.
The issue is that they are trying to optimize string concatenation
operations. When you call toString on a StringBuffer, it doesn't create a
new character array for use by the String object, it simply uses the one
that was created by the StringBuffer. The copy isn't made until the next
time you modify the StringBuffer. Usually, once you create the String you
are done with the StringBuffer, so it makes sense to optimize this case. But
it leads to some gotchas.
When you modify a StringBuffer, it doesn't usually shrink the size of the
char array. So let's say we do this:
Vector v = new Vector();
StringBuffer sb = new StringBuffer( 100000 );
for( int i = 0; i < 5000; i++ )
{
sb.append( "a" );
v.add( sb.toString() );
sb.delete(0, sb.length());
}
This creates a vector containing 5000 single character strings, so this
should have no problem, right? We made the initial StringBuffer a bit larger
than we needed, but what does that matter?
On my machine that dies, running out of memory. We end up trying to create
5000 strings that have a 100000 character array of which we only use one
character. This is the kind of thing that the heap analysis tool is pointing
out.
If you are going to reuse the StringBuffer you need to setLength( 0 ). They
have a special optimization in there for this case. It has this comment:
// If newLength is zero, assume the StringBuffer is being
// stripped for reuse; Make new buffer of default size
This will shrink the StringBuffer array back down to its default size of 16.
Using that in the above code will create 1 string of 100000 and 4999 of 16
chars.
setLength(0) is the only operation that will actually shrink the array size
of the StringBuffer's character array and the size of the char array will
also be the size of the char array in a toString operation. Calling
setLength( 0 ) seems the only safe way to reuse a StringBuffer.
But this relies on that optimization being present in setLength( 0 ). Is
this a valid assumption? Apparently not. Here is a message about Kaffe,
which did not have this setLength optimization:
http://www.kaffe.org/pipermail/kaffe/2000-March/037964.html
But apparently Sun fell victim to this too. Apparently in 1.4.1 they
eliminated this setLength optimization and had to put it back in for 1.4.2.
That means that it can't be relied on, so you cannot assume the optimization
is there. The bug report also says that this may change yet again in 1.5.
See:
http://www.javaspecialists.co.za/archive/Issue068.html at the bottom
http://developer.java.sun.com/developer/bugParade/bugs/4724129.html
So the advice is never, ever, reuse a StringBuffer after you call toString
on it. It used to be safe if you called setLength(0) on it, but that cannot
be relied upon any more.
There are also some similar gotchas on substring calls on String, because it
shares memory as well.