Reputation: 967
We are facing very bad performance hit in our application due to large number of strings : created to log application state. We are planing to move to String builder at least for the logger. One confusion I have is :
As string builder is called as such :
StringBuilder sb = new StringBuilder();
sb.append("Very big String object....");
If I am right , "Very big String object...." is a still a constructor creating very large String object(immutable) in memory, which remain in String pool.
So whats the use of String builder than in this case ?, as it will certainly create one more object , that will be though garbage collected.
But the String that is created using String constructor (double qoutes) "Very big String object....", is still in memory pool.
Upvotes: 0
Views: 470
Reputation: 3484
StringBuilder
improves memory consumption and performance for multiple additions. Lets analyze next example (imagine that javac doesn't optimize any String
concatenations):
String s = "a" + "b" + "c" + "d" + ... + "z";
StringBuilder sb = new StringBuilder("a").append("b").append("c")....append("z");
In case of String
concatenation with +
java will add strings from left to right, creating a new string each time: "ab"
, then "abc"
, then "abcd"
, thus 25 new strings, and each time it will copy the previous result completely. While StringBuilder
will simply add each string to its own char[]
array, without any redundant objects creation.
Now let n
be the number of strings, and l
- the length of each string. In this case the complexity of the m
th +
will be O(l*m)
, because each time the whole previous strings concatenation is copied. Thus we can make a conclusion that summary time (and memory (!)) complexity will be O(l*n*n)
for String
case. While in the case of StringBuilder
it will be O(l*n)
.
Also regarding logging - small performance comparison:
@Benchmark
public void stringConcatenation(Blackhole bh) {
// By using all these string we should prevent string builder optimizations.
String a = "start ";
String b = a + "first";
String c = b + " inside ";
String d = c + "second";
String e = d + ", ";
String f = e + 1024;
bh.consume(a);
bh.consume(b);
bh.consume(c);
bh.consume(d);
bh.consume(e);
bh.consume(f);
}
@Benchmark
public void stringBuilder(Blackhole bh) {
StringBuilder sb = new StringBuilder("start ")
.append("first")
.append(" inside ")
.append("second")
.append(", ")
.append(1024);
bh.consume(sb.toString());
}
@Benchmark
public void logback(Blackhole bh) {
// Logback formatting
bh.consume(MessageFormatter.arrayFormat("start {} inside {}, {}", new Object[]{"first", "second", 1024}).getMessage());
}
@Benchmark
public void log4j(Blackhole bh) {
// Log4j formatting
bh.consume(messageFactory.newMessage("start {} inside {}, {}", "first", "second", 1024));
}
And the results:
Benchmark Mode Cnt Score Error Units
LogBenchmark.stringConcatenation thrpt 5 9080147,269 ? 988134,269 ops/s
LogBenchmark.stringBuilder thrpt 5 27136050,849 ? 2776464,863 ops/s
LogBenchmark.logback thrpt 5 3579746,331 ? 346554,072 ops/s
LogBenchmark.log4j thrpt 5 4992342,169 ? 335971,537 ops/s
So as you can see suggested by some guys "use logging framework formatter instead" may not be the better choice if you are actually logging a lot.
Upvotes: 2
Reputation: 1781
StringBuilder
class is useful when you want to build a string by concatenating values, and new values are added in different sentences of the code:
StringBuilder sb = new StringBuilder("Counting from 0 to 9: ");
for(int i = 0 ; i < 10 ; i++) {
sb.append(i).append(' ');
}
System.out.println(sb.toString());
This class is used because, in this example, the java string concatenation operator +
would provide poor performance.
In your example, you don't need a StringBuilder
, since you are constructing a static, inmutable, string object.
Upvotes: 1
Reputation: 77177
You're not right. "Very big String object...."
is a compile-time constant, not a constructor, and the String
object representing it will go into the constant pool to be reused every time it's needed. The objects that will be created are the actual builder, its backing character array, and the String
produced after you add your log information--all of which will vary with each output statement.
If you were only ever using a fixed output string, then using a StringBuilder
wouldn't make sense, but the point of the builder is for the parts that vary.
All that said, if logging is a real performance problem, either the String
creation isn't the real bottleneck (and actual I/O for the log messages is), or you're building the log messages even if they're not output (e.g., for DEBUG
level log statements). In the latter case, you should simply avoid building them entirely if the logger is disabled; modern log frameworks such as slf4j do this automatically (log.debug()
in slf4j simply exits immediately if DEBUG
logging is turned off).
Upvotes: 2