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();