如何关闭字符串连接优化

问题描述 投票:0回答:2

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

java concatenation string-concatenation
2个回答
1
投票

字符串连接功能是在Java 9中的JEP 208中定义的。它由StringConcatFactory类(javadoc)控制。这是负责在运行时进行String串联的类,因此,字符串串联由StringConcatFactory::makeConcat在运行时处理。

[StringConcatFactory定义了几种以Strategy枚举(source code)形式生成字节码的策略。

您可以通过设置-Djava.lang.invoke.stringConcat从命令行更改默认策略

要在运行时获得Java-8行为,需要将其设置为BC_SB,它代表“字节码,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

但是,正如Kayaman正确指出的那样,这会在运行时影响程序。无论字符串在何处,编译器仍将生成invokedynamicStringConcatFactory

禁用此行为以在编译时禁用此行为的最直接方法是将--release标志传递给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);
    }
}

如果我们编译时没有任何标志,我们将得到:

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
}

注意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
}

这里String的连接是使用StringBuilder完成的,就像Java 8中一样。


0
投票

正如Malt所指出的,您可以使用命令行参数来控制策略。但是,这会影响java,而不影响javac。无法(AFAIK)从编译中禁用它。

但是,如果您可以将compiling与Java 8结合使用,则无论您在哪个版本上运行它,优化都不会发生。如果您不相信在运行时给出了正确的标志,则可以使用Java 8进行编译,直到提出其他解决方案为止(例如,用显式的"" + foo代码替换所有StringBuilder,尽管您可能不是这样)想一起去)。

© www.soinside.com 2019 - 2024. All rights reserved.