In Java 9 Oracle improved String concatenation. Now "" + someBoolean
turns into invokedynamic
with StringConcatFabric.makeConcat
as bootstrap method. That fabric generates classes at runtime that concatenate your strings. I want to disable this behaviour and fallback to plain old string builder.
So I supposed that javac has flag that do what I want. But I can't find it.
There are two parts to the string concatenation feature.
At runtime
In Java 9+, at runtime, String concatenation is controlled by the
StringConcatFactory
class (javadoc). That's becausejavac
generatesinvokedynamic
bytecode toStringConcatFactory::makeConcat
wherever String concatenation is needed.StringConcatFactory
defines several strategies for runtime concatenation in the form of aStrategy
enum (source code).You can change the default strategy from the command line by setting
-Djava.lang.invoke.stringConcat
To get the Java-8 behavior at runtime, you need to set it to
BC_SB
, which stands for "Bytecode, StringBuilder"Here are the other values, for completeness:
/** * 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
At compile time
As Kayaman correctly notes, the
StringConcatFactory
affects the program at runtime only. The bytecode will still contain aninvokedynamic
toStringConcatFactory
wherever Strings are concatenated. There are several ways of getting back the calls toStringBuilder
:The most straightforward approach of disabling this behavior is to pass the
--release=8
flag to javac to force the generation of Java-8 compatible code. However, this affects not only string concatenation.A more targeted option is to control concatenation specifically, by passing
-XDstringConcat=inline
.Let's take this piece of code as an example:
public class Print { public static void main(String[] args) { String foo = "a"; String bar = "b"; System.out.println(foo+bar); } }
If we compile it without any flags, we'll get:
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 }
Note the
invokedynamic
tomakeConcatWithConstants
.However, if we run
javac -XDstringConcat=inline Print.java
, we'll get this: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 }
Here the
String
concatenation is done using theStringBuilder
, just like in Java 8.
As of Java 15, StringConcatFactory
provides no other strategies for concatenation than MH_INLINE_SIZED_EXACT
. See this thread in the mailing lists and this bug for details.
As Malt has already pointed out in his answer, you can now only get back to StringBuilder
s by disabling use of invokedynamic
at compile time. To do this, pass the -XDstringConcat=inline
flag to the compiler. More details in the above referenced Malt's answer.
If working with JAR files from Maven Central, you will need to recompile them yourself.
© 2022 - 2024 — McMap. All rights reserved.
Proxy
? No access to annotations allowed? Even the XML parsers/XSLT processors use runtime class generation and who knows what else uses it as well. – Campobello