如何关闭字符串串联优化

Юри*_*нов 3 java jvm concatenation javac string-concatenation

在Java 9中,Oracle改进了String连接。现在"" + someBoolean变成invokedynamic带有StringConcatFabric.makeConcatbootstrap方法。该结构在运行时生成将您的字符串连接在一起的类。我想禁用此行为并回退到普通的旧字符串生成器。
因此,我认为javac具有执行我想要的功能的标志。但是我找不到。

Mal*_*alt 5

字符串串联功能分为两部分。

  1. 在运行时

    在Java 9+中,在运行时,字符串连接由StringConcatFactory类(javadoc)控制。那是因为在需要字符串连接的任何地方都会javac生成invokedynamic字节码到StringConcatFactory :: makeConcat`。

    StringConcatFactory以枚举(源代码)的形式定义了几种用于运行时连接的策略。Strategy

    您可以通过以下方式从命令行更改默认策略: -Djava.lang.invoke.stringConcat

    要在运行时获取Java-8行为,您需要将其设置为BC_SB,代表“ Bytecode,StringBuilder”

    为了完整性,以下是其他值:

    /**
     * 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
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在编译时

    正如Kayaman正确指出的那样,这种StringConcatFactory影响仅在运行时影响程序。字节码仍然会包含invokedynamicStringConcatFactory哪里字符串连。有几种方法可以将呼叫重新发送给StringBuilder

    • 禁用此行为的最直接方法是将--release=8标志传递给javac,以强制生成Java-8兼容代码。但是,这不仅影响字符串连接。

    • 更具针对性的选项是通过来专门控制串联-XDstringConcat=inline

      让我们以这段代码为例:

      public class Print {    
          public static void main(String[] args) {
              String foo = "a";
              String bar = "b";
              System.out.println(foo+bar);
          }
      }
      
      Run Code Online (Sandbox Code Playgroud)

      如果不带任何标志就对其进行编译,则会得到:

      public class Print {
        public Print();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
      
        public static void main(java.lang.String[]);
          Code:
             0: ldc           #2                  // String a
             2: astore_1
             3: ldc           #3                  // String b
             5: astore_2
             6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
             9: aload_1
            10: aload_2
            11: invokedynamic #5,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
            16: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            19: return
      }
      
      Run Code Online (Sandbox Code Playgroud)

      注意invokedynamicmakeConcatWithConstants

      但是,如果运行javac -XDstringConcat=inline Print.java,则会得到以下信息:

      public class Print {
        public Print();
          Code:
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
      
        public static void main(java.lang.String[]);
          Code:
             0: ldc           #2                  // String a
             2: astore_1
             3: ldc           #3                  // String b
             5: astore_2
             6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
             9: new           #5                  // class java/lang/StringBuilder
            12: dup
            13: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V
            16: aload_1
            17: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            20: aload_2
            21: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
            24: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
            27: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            30: return
      }
      
      Run Code Online (Sandbox Code Playgroud)

      就像Java 8一样,这里String是使用进行连接的StringBuilder