如JEP 280 中所写: Indify String Concatenation:
更改
String
生成的静态连接字节码序列javac
以使用invokedynamic
对 JDK 库函数的调用。这将使未来的String
连接优化成为可能,而无需进一步更改javac
.
在这里我想了解invokedynamic
调用的用途是什么以及字节码连接有什么不同invokedynamic
?
如JEP 280 中所写: Indify String Concatenation:
更改
String
生成的静态连接字节码序列javac
以使用invokedynamic
对 JDK 库函数的调用。这将使未来的String
连接优化成为可能,而无需进一步更改javac
.
在这里我想了解invokedynamic
调用的用途是什么以及字节码连接有什么不同invokedynamic
?
“旧”方式输出一堆StringBuilder
面向的操作。考虑这个程序:
public class Example {
public static void main(String[] args)
{
String result = args[0] + "-" + args[1] + "-" + args[2];
System.out.println(result);
}
}
如果我们使用 JDK 8 或更早版本编译它,然后使用javap -c Example
它查看字节码,我们会看到如下内容:
公共类示例{ 公共示例(); 代码: 0:aload_0 1: invokespecial #1 // 方法 java/lang/Object."<init>":()V 4:返回 公共静态无效主(java.lang.String[]); 代码: 0: new #2 // 类 java/lang/StringBuilder 3:重复 4: invokespecial #3 // 方法 java/lang/StringBuilder."<init>":()V 7:aload_0 8:iconst_0 9:加载 10: invokevirtual #4 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 13: ldc #5 // 字符串 - 15: invokevirtual #4 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 18:加载_0 19:iconst_1 20:加载 21: invokevirtual #4 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: ldc #5 // 字符串 - 26: invokevirtual #4 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29:加载_0 30:图标st_2 31:加载 32: invokevirtual #4 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: invokevirtual #6 // 方法 java/lang/StringBuilder.toString:()Ljava/lang/String; 38:astore_1 39: getstatic #7 // 字段 java/lang/System.out:Ljava/io/PrintStream; 42:加载_1 43: invokevirtual #8 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 46:返回 }
如您所见,它创建一个StringBuilder
并使用append
. 这是著名的相当低效的内置缓冲区的默认容量StringBuilder
只有 16 个字符,并且编译器无法知道提前分配更多,因此最终不得不重新分配。它也是一堆方法调用。(请注意,JVM有时可以检测并重写这些调用模式以提高它们的效率。)
让我们看看 Java 9 生成了什么:
公共类示例{ 公共示例(); 代码: 0:aload_0 1: invokespecial #1 // 方法 java/lang/Object."<init>":()V 4:返回 公共静态无效主(java.lang.String[]); 代码: 0:aload_0 1:iconst_0 2:加载 3:aload_0 4:iconst_1 5:加载 6:aload_0 7:iconst_2 8:加载 9: invokedynamic #2, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 14:astore_1 15: getstatic #3 // 字段 java/lang/System.out:Ljava/io/PrintStream; 18:加载_1 19: invokevirtual #4 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 22:返回 }
哦,我的,但那更短。makeConcatWithConstants
:-) 它对from进行了一次调用StringConcatFactory
,这在其 Javadoc 中说明了这一点:
有助于创建字符串连接方法的方法,可用于有效连接已知类型的已知数量的参数,可能在类型适应和参数的部分评估之后。这些方法通常用作呼叫站点的引导方法,以支持 Java 编程语言的字符串连接功能。
invokedynamic
Before going into the details of the invokedynamic
implementation used for optimisation of String concatenation, in my opinion, one must get some background over What's invokedynamic and how do I use it?
The
invokedynamic
instruction simplifies and potentially improves implementations of compilers and runtime systems for dynamic languages on the JVM. It does this by allowing the language implementer to define custom linkage behavior with theinvokedynamic
instruction which involves the following the below steps.
I would probably try and take you through these with the changes that were brought along for the implementation of String concatenation optimisation.
Defining the Bootstrap Method:-
With Java9, the bootstrap methods for invokedynamic
call sites, to support the string concatenation primarily makeConcat
and makeConcatWithConstants
were introduced with the StringConcatFactory
implementation.
The use of invokedynamic provides an alternative to select a translation strategy until runtime. The translation strategy used in StringConcatFactory
is similar to the LambdaMetafactory
as introduced in the previous java version. Additionally one of the goals of the JEP mentioned in the question is to stretch these strategies further.
Specifying Constant Pool Entries:- These are the additional static arguments to the invokedynamic
instruction other than (1) MethodHandles.Lookup
object which is a factory for creating method handles in the context of the invokedynamic
instruction,(2) a String
object, the method name mentioned in the dynamic call site and (3) the MethodType
object, the resolved type signature of the dynamic call site.
There are already linked during the linkage of the code. At runtime, the bootstrap method runs and links in the actual code doing the concatenation. It rewrites the invokedynamic
call with an appropriate invokestatic
call. This loads the constant string from the constant pool, the bootstrap method static args are leveraged to pass these and other constants straight to the bootstrap method call.
Using the invokedynamic Instruction:- This offers the facilities for a lazy linkage, by providing the means to bootstrap the call target once, during the initial invocation. The concrete idea for optimisation here is to replace the entire StringBuilder.append
dance with a simple invokedynamic
call to java.lang.invoke.StringConcatFactory
, that will accept the values in the need of concatenation.
The Indify String Concatenation proposal states with an example the benchmarking of the application with Java9 where a similar method as shared by @T.J. Crowder is compiled and the difference in the bytecode is fairly visible between the varying implementation.
我将在这里稍微添加一些细节。要获得的主要部分是字符串连接的完成方式是运行时决定,而不是编译时决定。因此它可以更改,这意味着您已经针对 java-9 编译了一次代码,并且它可以随意更改底层实现,而无需重新编译。
第二点是目前有6 possible strategies for concatenation of String
:
private enum Strategy {
/**
* 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
}
您可以通过参数选择其中任何一个:-Djava.lang.invoke.stringConcat
。请注意,这StringBuilder
仍然是一个选项。