2

In my application there was a heap dump and surprisingly heap retained by char[] was around 700MB, which was strange (at least for me). At the same time String had only 150MB.

In my application, I have only used StringBuilder (using default StringBuilder constructor) and tried to avoid using String as we were appending data.

My question here is: Should we always go for StringBuilder? And if yes, how can we reduce the heap retained by it?

mastov
  • 2,942
  • 1
  • 16
  • 33
sauumum
  • 1,638
  • 1
  • 19
  • 36
  • Related: http://stackoverflow.com/q/13360229/4271479 – mastov May 19 '16 at 09:34
  • If you *always* use the default constructor of `StringBuilder`, it will potentially need to keep on allocating larger `char[]` instances internally as you fill them up (requiring roughly twice the space as if the know the eventual size up-front). But probably more importantly, when you build the final `String`, the `char[]` in the `StringBuilder` has to be duplicated. – Andy Turner May 19 '16 at 09:34
  • You may want to get the finished `String` out of the `StringBuilder`, when you are done appending, and throw the `StringBuilder` away. – mastov May 19 '16 at 09:35
  • @Andy Turner : We have all String variable declared as final as all of them are local variable. – sauumum May 19 '16 at 10:06
  • I meant final as in "eventual", rather than `final`. A copy of the `char[]` in the `StringBuilder` must be made because it remains mutable, but `String` is immutable (same reason `String.toCharArray()` returns a copy). – Andy Turner May 19 '16 at 10:07
  • @mastov : I am making every [StringBuilder] null once we are reading String out from it. Even though [StringBuilder] is ready for garbage collection, but it will be only collected whenever JVM wants, this is why we have so much memory occupied by char[]. – sauumum May 19 '16 at 10:08
  • 1
    @SauravKumarMehta: If that's the reason for the big footprint, then I wouldn't worry about it, since the memory is effectively "made available" (even if it's still in the physical memory) because it *can* be garbage-collected whenever necessary. – mastov May 19 '16 at 10:13

2 Answers2

0

Possibly you can use StringInterner which takes a StringBuilder to avoid creating objects needlessly.You first populate a recycled StringBuilder with the text and if a String matching that text is in the interner, that String is returned (or a toString() of the StringBuilder is.) The benefit is that you only create objects (and no more than needed) when you see a new String (or at least one not in the array) This can get a 80% to 99% hit rate and reduce memory consumption (and garbage) dramatically when loading many strings of data.

Code: https://github.com/OpenHFT/Java-Lang/blob/master/lang/src/main/java/net/openhft/lang/pool/StringInterner.java

Sanjit Kumar Mishra
  • 1,153
  • 13
  • 32
  • At this moment we don't want to add another dependency for 3rd party jar, but it wil be interesting to see what StringInterner does ? – sauumum May 19 '16 at 10:10
  • Have a look into this dicusiion, http://stackoverflow.com/questions/26290043/optimizing-java-heap-usage-by-string-using-stringbuffer-stringbuilder-string – Sanjit Kumar Mishra May 19 '16 at 10:34
0

Yes, always go for StringBuilder when building strings - it's the most efficient, but still convenient, way of concatenating strings.

It sounds like there are lots of StringBuilders hanging around waiting to be garbage collected. However, to reduce heap usage, you can safely reuse your StringBuilders even though they are not threadsafe by using one StringBuilder per thread via Threadlocal:

private static final ThreadLocal<StringBuilder> LOCAL_STRING_BUILDER =
    ThreadLocal.withInitial(StringBuilder::new);

Example usage:

public String logMessage() {
    StringBuilder sb = LOCAL_STRING_BUILDER.get();
    sb.setLength(0); // Only resets the pointer to start. Doesn't affect the backing array
    sb.append("foo=").append(myField); //etc
    return sb.toString();
}

You will only ever have at most as many StringBuilders as there are threads, which won't be that many (maybe 10's - 100's).


FYI StringBuilder is used when concatenating strings manually anyway; this line of source:

String str3 = str1 + str2;

gets compiled as if it were:

String str3 = new StringBuilder().append(str1).append(str2).toString();
Bohemian
  • 412,405
  • 93
  • 575
  • 722