0

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.

1
  • 2
    1. Parameterized logging should be used to avoid creating such objects instance which are not required at run-time. 2. Employ Dynamic Log Level Change in your application to effectively utilize the benefit of DEBUG/INFO level logings. Commented Jan 11, 2016 at 11:31

3 Answers 3

2

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).

Sign up to request clarification or add additional context in comments.

2 Comments

Even if DEBUG level is set to ERROR: in log.debug( "Very long string....") , "Very long string...." constructor will still be executed and memory will be consumed for it.
@user3769778 You're wrong. There is only ever one copy of that constant string. It's irrelevant how many times it's used.
2

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.

2 Comments

That would be nice to see there log4j 2 too:)
Thx, interesting results
1

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.

3 Comments

With java 8 StringJoiner would be better option for most cases, also I read somewhere which I dont remember that after java 8 even '+' operations converted to StringBuilder on background by JVM
@HRgiger AFAIK, the '+' operator is converted to StringBuilder when all the chunks are in the same operation. E.g.: String newString = "The value is " + value + ". Goodbye";
possible I need to do some readings for that:) (i am not the downvoter)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.