Why is string concatenation in a loop not optimized to StringBuilder.append?

I did a quick experiment to test out the performance of string concatenation vs StringBuilder with OpenJDK 14.

Without StringBuilder

class StringConcatNoBuilder {
  public static void main(String[] args) {
    final int numIterations = Integer.parseInt(args[0]);
    String s = "";
    for (int i = 0; i < numIterations; ++i) {
      s += "abcdefg";
    }
    System.out.println("Finished");
  }
}

With StringBuilder

class StringConcatWithBuilder {
  public static void main(String[] args) {
    final int numIterations = Integer.parseInt(args[0]);
    final StringBuilder builder = new StringBuiler();
    for (int i = 0; i < numIterations; ++i) {
      builder.append("abcdefg");
    }
    final String s = builder.toString();
    System.out.println("Finished");
  }
}

Timing Analysis

I ran each program for numIterations = 1000000. The StringBuilder program terminated almost immediately and the non StringBuilder program failed to terminate even after several minutes.

Needless to say, no optimization is happening at the bytecode level.

Open question

It seems like a fairly low hanging optimization fruit for the compiler to turn this:

String s = "";
for (int i = 0; i < numIterations; ++i) {
  s += "abcdefg";
}

into this:

StringBuilder builder = new StringBuilder();
for (int i = 0; i < numIterations; ++i) {
  builder.append("abcdefg");
}
String s = builder.toString();

Yet it doesn’t. Why?

Written on May 25, 2021