java - Java 9 中的字符串连接是如何实现的?

标签 java string string-concatenation java-9 invokedynamic

写在JEP 280: Indify String Concatenation :

Change the static String-concatenation bytecode sequence generated by javac to use invokedynamic calls to JDK library functions. This will enable future optimizations of String concatenation without requiring further changes to the bytecode emmited by 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 查看字节码,我们会看到如下内容:

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
}

如您所见,它创建了一个 StringBuilder 并使用了 append。这是众所周知的相当低效的做法,因为 StringBuilder 中内置缓冲区的默认容量仅为 16 个字符,编译器 无法提前知道分配更多字符,所以它最终不得不重新分配。它也是一堆方法调用。 (不过请注意,JVM 有时 可以检测并重写这些调用模式以提高它们的效率。)

让我们看看 Java 9 生成了什么:

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
}

哦,天哪,但那更短了。 :-) 它从 StringConcatFactory 调用 makeConcatWithConstants ,在它的 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 - Java 9 中的字符串连接是如何实现的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61918877/

相关文章:

Java:JavaMail、POP3 和一些问题

java - Servlet 容器和类路径

c++ - 这段代码中如何使用 map<string, set<string>>?

java - 父类方法如何返回子类类型?这不违反继承吗?

c# - 使用字符串格式化与字符串连接相比有什么好处吗?

javascript - 如何在 JavaScript 中将常数加到 innerWidth?

jsf - 如何在EL表达式中串联字符串?

java - 动态创建新的滚动文件 LOG4j2 2.6

java - 应用程序小部件和 Activity 堆栈

jquery - 获取当前的 jQuery 选择器字符串?