Java 9 中如何实现字符串连接? [英] How is String concatenation implemented in Java 9?
问题描述
如JEP 280:指示字符串连接中所述:
<块引用>更改由javac
生成的静态String
-concatenation 字节码序列,以使用invokedynamic
调用JDK 库函数.这将启用 String
连接的未来优化,而无需进一步更改 javac
发出的字节码.
这里我想了解一下invokedynamic
调用的用途是什么,字节码拼接和invokedynamic
有什么不同?
老"的方式输出了一堆面向StringBuilder
的操作.考虑这个程序:
公共类示例{public static void main(String[] args){字符串结果 = args[0] + "-" + args[1] + "-" + args[2];System.out.println(结果);}}
如果我们使用 JDK 8 或更早版本编译它,然后使用 javap -c Example
查看字节码,我们会看到如下内容:
如您所见,它创建了一个 StringBuilder
并使用了 append
.这是众所周知的相当低效的,因为 StringBuilder
中内置缓冲区的默认容量只有 16 个字符,并且 编译器 无法知道提前分配更多,所以它最终不得不重新分配.这也是一堆方法调用.(请注意,JVM 可以有时检测并重写这些调用模式,以提高它们的效率.)
让我们看看 Java 9 生成了什么:
<前>公共类示例{公共示例();代码:0:aload_01: invokespecial #1//方法 java/lang/Object."<init>":()V4:返回public static void main(java.lang.String[]);代码:0:aload_01:iconst_02:加载3:aload_04:iconst_15:加载6:aload_07:iconst_28:加载9:invokeDynamic #2, 0//InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;14:astore_115: getstatic #3//字段 java/lang/System.out:Ljava/io/PrintStream;18:aload_119: invokevirtual #4//方法 java/io/PrintStream.println:(Ljava/lang/String;)V22:返回}哦,天哪,但那更短.:-) 它从 StringConcatFactory
,在它的 Javadoc 中是这样说的:
有助于创建字符串连接方法的方法,可用于有效连接已知类型的已知数量的参数,可能在类型适应和参数的部分评估之后.这些方法通常用作invokedynamic
调用站点的引导方法,以支持Java 编程语言的字符串连接特性.
As written in JEP 280: Indify String Concatenation:
Change the static
String
-concatenation bytecode sequence generated byjavac
to useinvokedynamic
calls to JDK library functions. This will enable future optimizations ofString
concatenation without requiring further changes to the bytecode emmited byjavac
.
Here I want to understand what the use of invokedynamic
calls is and how bytecode concatenation is different from invokedynamic
?
The "old" way output a bunch of StringBuilder
-oriented operations. Consider this program:
public class Example {
public static void main(String[] args)
{
String result = args[0] + "-" + args[1] + "-" + args[2];
System.out.println(result);
}
}
If we compile that with JDK 8 or earlier and then use javap -c Example
to see the bytecode, we see something like this:
public class Example { public Example(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/StringBuilder 3: dup 4: invokespecial #3 // Method java/lang/StringBuilder."<init>":()V 7: aload_0 8: iconst_0 9: aaload 10: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 13: ldc #5 // String - 15: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 18: aload_0 19: iconst_1 20: aaload 21: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: ldc #5 // String - 26: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29: aload_0 30: iconst_2 31: aaload 32: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 35: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 38: astore_1 39: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; 42: aload_1 43: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 46: return }
As you can see, it creates a StringBuilder
and uses append
. This is famous fairly inefficient as the default capacity of the built-in buffer in StringBuilder
is only 16 chars, and there's no way for the compiler to know to allocate more in advance, so it ends up having to reallocate. It's also a bunch of method calls. (Note that the JVM can sometimes detect and rewrite these patterns of calls to make them more efficient, though.)
Let's look at what Java 9 generates:
public class Example { public Example(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: aload_0 1: iconst_0 2: aaload 3: aload_0 4: iconst_1 5: aaload 6: aload_0 7: iconst_2 8: aaload 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 // Field java/lang/System.out:Ljava/io/PrintStream; 18: aload_1 19: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 22: return }
Oh my but that's shorter. :-) It makes a single call to makeConcatWithConstants
from StringConcatFactory
, which says this in its Javadoc:
Methods to facilitate the creation of String concatenation methods, that can be used to efficiently concatenate a known number of arguments of known types, possibly after type adaptation and partial evaluation of arguments. These methods are typically used as bootstrap methods for
invokedynamic
call sites, to support the string concatenation feature of the Java Programming Language.
这篇关于Java 9 中如何实现字符串连接?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!