Hyrio
Hyrio

Reputation: 231

Optimizations of "+" operand for strings in JDK 17

We know that in JDK8 and below, strings using a plus sign for concatenation will be compiled into StringBuilder for performance optimization, but after JDK9, it will be implemented using the java.lang.invoke.StringConcatFactory#makeConcatWithConstants method.

However, after decompiling java.lang.Object, it can be seen that its toString method is still implemented using StringBuilder:

public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
  stack=2, locals=1, args_size=1
     0: new           #1                  // class java/lang/StringBuilder
     3: dup
     4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
     7: aload_0
     8: invokevirtual #7                  // Method getClass:()Ljava/lang/Class;
    11: invokevirtual #13                 // Method java/lang/Class.getName:()Ljava/lang/String;
    14: invokevirtual #19                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    17: ldc           #23                 // String @
    19: invokevirtual #19                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    22: aload_0
    23: invokevirtual #25                 // Method hashCode:()I
    26: invokestatic  #29                 // Method java/lang/Integer.toHexString:(I)Ljava/lang/String;
    29: invokevirtual #19                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
    32: invokevirtual #35                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
    35: areturn
  LineNumberTable:
    line 256: 0
  LocalVariableTable:
    Start  Length  Slot  Name   Signature
        0      36     0  this   Ljava/lang/Object;

Looking through the source code, it can be seen that it still uses the plus sign to connect the three parts:

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

Rewrite the same code into a class to compile:

public class Main {
    public String fooString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
}

It can be seen from the decompilation that it is still implemented using the java.lang.invoke.StringConcatFactory#makeConcatWithConstants method:

public java.lang.String fooString();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
        start local 0 // Main this
         0: aload_0
         1: invokevirtual #7                  // Method java/lang/Object.getClass:()Ljava/lang/Class;
         4: invokevirtual #11                 // Method java/lang/Class.getName:()Ljava/lang/String;
         7: aload_0
         8: invokevirtual #17                 // Method java/lang/Object.hashCode:()I
        11: invokestatic  #21                 // Method java/lang/Integer.toHexString:(I)Ljava/lang/String;
        14: invokedynamic #27,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        19: areturn
        end local 0 // Main this
      LineNumberTable:
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      20     0  this   LMain;

So why is the plus sign used in Object class implemented using StringBuilder while class A(mentioned above) using makeConcatWithConstants? Does the bytecode of the Object class provided by the JDK do not match the source code, or is there a special optimization for the Object class at compile time?

Upvotes: 23

Views: 2440

Answers (1)

Michael
Michael

Reputation: 44220

It's explained in the JEP. Emphasis mine

java.base exemptions. Since this feature uses a core library feature (java.lang.invoke) to implement a core language feature (String concatenation), we have to exempt the java.base module from using the indified String concat. Otherwise a circularity happens when the java.lang.invoke.* machinery requires String concat to work, which in turn requires the java.lang.invoke.* machinery. This exemption may limit the performance improvements observed from this feature, since many java.base classes will not be able to use it. We consider this an acceptable downside, which should be covered by the VM's optimizing compilers.

Upvotes: 24

Related Questions