如何关闭字符串连接优化
How to turn off string concatenation optimization
In Java 9 Oracle 改进了字符串连接。现在 "" + someBoolean
变成 invokedynamic
和 StringConcatFabric.makeConcat
作为 bootstrap 方法。该结构在运行时生成 类 以连接您的字符串。我想禁用此行为并回退到普通的旧字符串生成器。
所以我认为 javac 有标志可以做我想做的事。但我找不到它。
字符串连接功能有两个部分。
在运行时间
在 Java 9+ 中,在 运行 时,字符串连接由 StringConcatFactory
class (javadoc) 控制。这是因为 javac
在需要字符串连接的地方生成 invokedynamic
字节码到 StringConcatFactory::makeConcat
。
StringConcatFactory
以 Strategy
枚举 (source code) 的形式为 运行time 连接定义了几种策略。
您可以通过设置 -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
编译时
正如 Kayaman 正确指出的那样,StringConcatFactory
仅在 运行 时间 影响程序。在连接字符串的任何地方,字节码仍将包含 invokedynamic
到 StringConcatFactory
。有几种方法可以返回对 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);
}
}
如果我们在没有任何标志的情况下编译它,我们将得到:
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
}
注意 invokedynamic
到 makeConcatWithConstants
。
但是,如果我们 运行 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.
从 Java 15 开始,StringConcatFactory
除了 MH_INLINE_SIZED_EXACT
之外没有提供其他串联策略。有关详细信息,请参阅 this thread in the mailing lists and this bug。
正如 Malt 在 中指出的那样,您现在只能通过在编译时禁用 invokedynamic
来返回到 StringBuilder
s。为此,将 -XDstringConcat=inline
标志传递给编译器。上面引用的 Malt 的回答中有更多详细信息。
如果使用来自 Maven Central 的 JAR 文件,you will need to recompile them yourself。
In Java 9 Oracle 改进了字符串连接。现在 "" + someBoolean
变成 invokedynamic
和 StringConcatFabric.makeConcat
作为 bootstrap 方法。该结构在运行时生成 类 以连接您的字符串。我想禁用此行为并回退到普通的旧字符串生成器。
所以我认为 javac 有标志可以做我想做的事。但我找不到它。
字符串连接功能有两个部分。
在运行时间
在 Java 9+ 中,在 运行 时,字符串连接由
StringConcatFactory
class (javadoc) 控制。这是因为javac
在需要字符串连接的地方生成invokedynamic
字节码到StringConcatFactory::makeConcat
。StringConcatFactory
以Strategy
枚举 (source code) 的形式为 运行time 连接定义了几种策略。您可以通过设置
从命令行更改默认策略-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
编译时
正如 Kayaman 正确指出的那样,
的调用StringConcatFactory
仅在 运行 时间 影响程序。在连接字符串的任何地方,字节码仍将包含invokedynamic
到StringConcatFactory
。有几种方法可以返回对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); } }
如果我们在没有任何标志的情况下编译它,我们将得到:
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 }
注意
invokedynamic
到makeConcatWithConstants
。但是,如果我们 运行
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.
从 Java 15 开始,StringConcatFactory
除了 MH_INLINE_SIZED_EXACT
之外没有提供其他串联策略。有关详细信息,请参阅 this thread in the mailing lists and this bug。
正如 Malt 在 invokedynamic
来返回到 StringBuilder
s。为此,将 -XDstringConcat=inline
标志传递给编译器。上面引用的 Malt 的回答中有更多详细信息。
如果使用来自 Maven Central 的 JAR 文件,you will need to recompile them yourself。