Question

I'm aware of the difference between concat, StringBuffer and StringBuilder. I'm aware of the memory issue with the StringBuffer.toString backing array which can explode memory. I'm even aware of the JDK Sun optimization consisting in allocation a power of two for initial capacity.

But I'm still wondering about the best way to reuse a StringBuffer (used in toString()) or, if reusing the StringBuffer is pertinent. Which one is better with memory and speed performance in mind ?

public String toString2() {
  StringBuffer sb = new StringBuffer(<size>) 
  ... several .append(stuff) ...
  sb.trimToSize()
  return sb.toString()
}

or

private StringBuffer sb = new StringBuffer(<size>) 
public String toString2() {
  sb.delete()
  sb.setLength(1024)
  sb.trimToSize() 
  ... several .append(stuff) ...
  return sb.toString()
}

And why ?

Was it helpful?

Solution

  1. Use StringBuilder, a non-synchronized variant of StringBuffer -- that gives you a performance boost straight out of the box.
  2. No need to call trimToSize -- you are just forcing the StringBuilder to make a new char array.
  3. You'll save very, very little by reusing a StringBuilder since the JVM is highly optimized for the allocation of short-lived objects.
  4. Use StringBuilder instead of a simple concatenation expression only if you cannot express your string in a single expression -- for example, you are iterating over some stuff. Concatenation compiles to basically the same code using the StringBuilder.

OTHER TIPS

I would say the first example is definitely clearer, easier to read and safer - which should almost always trump performance micro-optimization concerns. If you do have a performance problem, and you can prove (with concrete measurements) that it is caused by this piece of code, and you can also prove by measurement that in your local environment, the 2nd example is significantly faster, then - and only then - you have a valid reason to use that.

Otherwise, there is no general answer to your question. What would it help you if people claimed that the 2nd example were faster by n% on average, if it doesn't make any difference on your machine (either because of a different environment, or because the actual piece of code is called so rarely in your app)?

However, my personal gut feeling is that on a modern JVM (read: at least on JDK6, but possibly already on JDK5), the first example might actually be faster, due to clever GC tricks making allocation / deallocation of short-lived objects extremely cheap. Especially so if you used a StringBuilder instead of StringBuffer, which has no synchronization overhead.

I wouldn't use StringBuffer at all esp if performance is a concern.

If you can't be sure that only one thread will call toString(), I would create a StringBuilder each time.

The other problem you have is that different data types can create a lot of garbage e.g. int or double and this can reduce any benefit you might achieve.

I wouldn't recycle StringBuilder, I would just keep things simple. If you really need the maximum performance I would use a different solution like a direct ByteBuffer with a library which creates no garbage.

Try performance testing both. As with all questions of performance it'll depend on the context and how the methods are called.

A couple of notes:

  • StringBuilder is not thread-safe, but in your 1st example thread-safety is not an issue as your method is not using an fields. This makes StringBuilder faster than StringBuffer.
  • Your second approach is not thread-safe, so if you have multiple threads calling the method, then both threads will use the safe StringBuffer, so you'll end up with a string that contains elements from both calls.
  • If you keeping the StringBuffer around (2nd example), depending on how often you use the method, you may actually be holding on to more memory because the garbage collector will not collect the memory used by the StringBuffer, which could be used by other objects.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top