JEP 280 (“ Indify String Concatenation”)是与JDK 9结合实现的,根据其“摘要”部分,“更改了javac生成的静态String
-concatenation字节码序列,以使用对JDK库函数的invokedynamic调用。 ” 通过查看在JDK 9之前和JDK 9之后的JDK中编译的使用字符串连接的类的javap输出,最容易看出这对Java中的字符串连接的影响。
以下名为“ HelloWorldStringConcat”的简单Java类将用于首次演示。
package dustin.examples;import static java.lang.System.out;public class HelloWorldStringConcat
{public static void main(final String[] arguments){out.println("Hello, " + arguments[0]);}
}
接下来显示的是使用JDK 8( AdoptOpenJDK )和JDK 11( Oracle OpenJDK )编译时, HelloWorldStringConcat
类的main(String)
方法从javap
输出的-verbose输出的差异。 我强调了一些关键差异。
JDK 8 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcat.classLast modified Jan 28, 2019; size 625 bytesMD5 checksum 3e270bafc795b47dbc2d42a41c8956afCompiled from "HelloWorldStringConcat.java"
public class dustin.examples.HelloWorldStringConcatminor version: 0major version: 52. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=4, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: new #3 // class java/lang/StringBuilder6: dup7: invokespecial #4 // Method java/lang/StringBuilder."":()V10: ldc #5 // String Hello,12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;15: aload_016: iconst_017: aaload18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V27: return
JDK 11 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcat.classLast modified Jan 28, 2019; size 908 bytesMD5 checksum 0e20fe09f6967ba96124abca10d3e36dCompiled from "HelloWorldStringConcat.java"
public class dustin.examples.HelloWorldStringConcatminor version: 0major version: 55. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=3, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: aload_04: iconst_05: aaload6: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;11: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V14: return
JEP 280的“描述”部分描述了这种区别:“其思想是用对java.lang.invoke.StringConcatFactory的简单invokedynamic
调用来替换整个StringBuilder
附加舞蹈,该调用将接受需要串联的值。” 同一部分显示了类似字符串连接示例的编译输出的相似比较。
JDK 11中用于简单字符串连接的编译输出不仅比其JDK 8对应的行少,而且还包含更多行。 它还具有较少的“昂贵”操作。 不需要包装原始类型,也不需要实例化大量额外的对象,就可以获得潜在的性能改进。 进行此更改的主要动机之一是“为构建优化的String
串联处理程序打下基础,无需更改Java到字节码编译器即可实现”,并“无需进一步更改String
串联即可启用将来的优化。 javac
发出的字节码。”
JEP 280不影响StringBuilder
或StringBuffer
有这样的使用方面一个有趣的暗示的StringBuffer (这我有困难的时候找到反正很好用 )和StringBuilder的 。 JEP 280被声明为“非目标”, 不要 “引入任何可能有助于构建更好的翻译策略的新的String
和/或StringBuilder
API”。 与此相关的是,对于简单的字符串连接(如本文开头的代码示例所示),显式使用StringBuilder
和StringBuffer
实际上将使编译器无法利用本文中讨论的JEP 280引入的功能。 。
接下来的两个代码清单显示了与上面显示的简单应用程序相似的实现,但是它们分别使用StringBuilder
和StringBuffer
代替字符串串联。 当使用JDK 8和JDK 11编译这些类后,对这些类执行javap -verbose
时, main(String[])
方法之间没有显着差异。
JDK 8和JDK 11中的显式StringBuilder
使用相同
package dustin.examples;import static java.lang.System.out;public class HelloWorldStringBuilder
{public static void main(final String[] arguments){out.println(new StringBuilder().append("Hello, ").append(arguments[0]).toString());}
}
HelloWorldStringBuilder.main(String[])
JDK 8 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuilder.classLast modified Jan 28, 2019; size 627 bytesMD5 checksum e7acc3bf0ff5220ba5142aed7a34070fCompiled from "HelloWorldStringBuilder.java"
public class dustin.examples.HelloWorldStringBuilderminor version: 0major version: 52. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=4, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: new #3 // class java/lang/StringBuilder6: dup7: invokespecial #4 // Method java/lang/StringBuilder."":()V10: ldc #5 // String Hello,12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;15: aload_016: iconst_017: aaload18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V27: return
HelloWorldStringBuilder.main(String[])
JDK 11 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuilder.classLast modified Jan 28, 2019; size 627 bytesMD5 checksum d04ee3735ce98eb6237885fac86620b4Compiled from "HelloWorldStringBuilder.java"
public class dustin.examples.HelloWorldStringBuilderminor version: 0major version: 55. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=4, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: new #3 // class java/lang/StringBuilder6: dup7: invokespecial #4 // Method java/lang/StringBuilder."":()V10: ldc #5 // String Hello,12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;15: aload_016: iconst_017: aaload18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V27: return
JDK 8和JDK 11中的显式StringBuffer
使用相同
package dustin.examples;import static java.lang.System.out;public class HelloWorldStringBuffer
{public static void main(final String[] arguments){out.println(new StringBuffer().append("Hello, ").append(arguments[0]).toString());}
}
HelloWorldStringBuffer.main(String[])
JDK 8 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuffer.classLast modified Jan 28, 2019; size 623 bytesMD5 checksum fdfb90497db6a3494289f2866b9a3a8bCompiled from "HelloWorldStringBuffer.java"
public class dustin.examples.HelloWorldStringBufferminor version: 0major version: 52. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=4, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: new #3 // class java/lang/StringBuffer6: dup7: invokespecial #4 // Method java/lang/StringBuffer."":()V10: ldc #5 // String Hello,12: invokevirtual #6 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;15: aload_016: iconst_017: aaload18: invokevirtual #6 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;21: invokevirtual #7 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V27: return
HelloWorldStringBuffer.main(String[])
JDK 11 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringBuffer.classLast modified Jan 28, 2019; size 623 bytesMD5 checksum e4a83b6bb799fd5478a65bc43e9af437Compiled from "HelloWorldStringBuffer.java"
public class dustin.examples.HelloWorldStringBufferminor version: 0major version: 55. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=4, locals=1, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: new #3 // class java/lang/StringBuffer6: dup7: invokespecial #4 // Method java/lang/StringBuffer."":()V10: ldc #5 // String Hello,12: invokevirtual #6 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;15: aload_016: iconst_017: aaload18: invokevirtual #6 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;21: invokevirtual #7 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;24: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V27: return
JDK 8和JDK 11处理循环字符串串联
对于我的JEP 280更改行为的最后一个示例,我使用一个代码示例,该示例可能会冒犯某些Java开发人员的敏感性,并在循环内执行字符串连接。 请记住,这只是一个示例,一切都可以,但不要在家中尝试。
package dustin.examples;import static java.lang.System.out;public class HelloWorldStringConcatComplex
{public static void main(final String[] arguments){String message = "Hello";for (int i=0; i<25; i++){message += i;}out.println(message);}
}
HelloWorldStringConcatComplex.main(String[])
JDK 8 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcatComplex.classLast modified Jan 30, 2019; size 766 bytesMD5 checksum 772c4a283c812d49451b5b756aef55f1Compiled from "HelloWorldStringConcatComplex.java"
public class dustin.examples.HelloWorldStringConcatComplexminor version: 0major version: 52. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=10: ldc #2 // String Hello2: astore_13: iconst_04: istore_25: iload_26: bipush 258: if_icmpge 3611: new #3 // class java/lang/StringBuilder14: dup15: invokespecial #4 // Method java/lang/StringBuilder."":()V18: aload_119: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;22: iload_223: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;26: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;29: astore_130: iinc 2, 133: goto 536: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;39: aload_140: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V43: return
HelloWorldStringConcatComplex.main(String[])
JDK 11 javap
输出
Classfile /C:/java/examples/helloWorld/classes/dustin/examples/HelloWorldStringConcatComplex.classLast modified Jan 30, 2019; size 1018 bytesMD5 checksum 967fef3e7625965ef060a831edb2a874Compiled from "HelloWorldStringConcatComplex.java"
public class dustin.examples.HelloWorldStringConcatComplexminor version: 0major version: 55. . .public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=10: ldc #2 // String Hello2: astore_13: iconst_04: istore_25: iload_26: bipush 258: if_icmpge 2511: aload_112: iload_213: invokedynamic #3, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;18: astore_119: iinc 2, 122: goto 525: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;28: aload_129: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V32: return
Heinz M. Kabutz博士和Dmitry Vyazelenko在演示文稿“ Java.lang.String足以 使我们自己吊起来…… ”中讨论了JEP 280引入的Java字符串连接更改,并对其进行了简要总结,“ +
不再编译为StringBuilder
。 ” 他们在“今天的经验教训”幻灯片中指出:“在可能的情况下使用+
代替StringBuilder
”和“为Java 9+重新编译类”。
在JDK 9中针对JEP 280实施的更改“将使String
连接的未来优化成为可能,而无需进一步更改javac
发出的字节码。” 有趣的是,最近宣布JEP 348 (“ JDK API的Java编译器内在特性”)现在是候选JEP,它旨在使用类似的方法来编译String::format
和Objects::hash
。
翻译自: https://www.javacodegeeks.com/2019/02/jdk-9-jep-280-string-concatenations.html