user3769778
user3769778

Reputation: 967

How does String builder create mutable object , when String is still created when using StringBuilder in Java?

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

Answers (3)

user3707125
user3707125

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 mth + 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

Mario
Mario

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

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

Related Questions