When was Javac StringBuilder/StringBuffer optimization introduced?

3.3k views Asked by At

I know that Javac compiler is able to transform String concatenation + using StringBuilder/StringBuffer, and I'm curious to know starting from which version this change was introduced?

I'm using this sample code:

public class Main {
  public static void main(String[] args) {
      String a = args[0];
      String s = "a";
      s = s + a;
      s = s + "b";
      s = s + "c";
      s = s + "d";
      s = s + "e";
      System.out.println(s);
  }
}

So far I've tried with javac 1.8.0_121, javac 1.6.0_20, javac 1.5.0_22 and java 1.4.2_19.

Here is a sample of the bytecode I see using javap -c from 1.4.2_19:

6:  astore_2
7:  new #3; //class StringBuffer
10: dup
11: invokespecial   #4; //Method java/lang/StringBuffer."<init>":()V
14: aload_2
15: invokevirtual   #5; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
18: aload_1
19: invokevirtual   #5; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
22: invokevirtual   #6; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;

All 4 versions seems to be using the StringBuilder/StringBuffer optimization, so I'm curious to know starting from which Javac version this change was introduced?

4

There are 4 answers

11
M A On BEST ANSWER

Here's a quote from the language specification from version 1:

An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class (§20.13) or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.

Back at the time, they had StringBuffer instead of StringBuilder.

Also a quote from StringBuffer of JDK1.0.2:

This Class is a growable buffer for characters. It is mainly used to create Strings. The compiler uses it to implement the "+" operator.

0
lukeg On

I have looked up the Java Language Specification, First Edition (from 1996). Not an easy find, but here it is. The passage on concatenation optimization was there even then:

An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class (§20.13) or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.

The specification pertained to StringBuffer then, but StringBuilder (which current JLS wording refers to) might be deemed better performing because its methods are not synchronized.

This, however, does not mean that one should rely on the optimization as always being in place. String concatenation in loops will not get optimized, for example.

1
Shubham Chaurasia On

JLS has already been given in some answers. I just want to make the point that StringBuffer (https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html) was there since 1.0 whereas

StringBuilder(https://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) came in version 1.5. Please see the since: section of respective javadocs.

2
Eugene On

This does not answer the question, but I want to merely add to the overall point that in jdk-9 this StringBuilder::append is one of the permitted strategies, but not the default one.

private enum Strategy {
   /**
    * Bytecode generator, calling into {@link java.lang.StringBuilder}.
    */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
     BC_SB_SIZED_EXACT,

   /**
    * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
    * This strategy also tries to estimate the required storage.
    */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
     MH_INLINE_SIZED_EXACT
}

It's actually an invokedynamic bytecode for String concatenation, so it's implementation is now JRE specific, not compiler one. The default strategy btw is : MH_INLINE_SIZED_EXACT