Java 9 中如何实现字符串连接? [英] How is String concatenation implemented in Java 9?

查看:29
本文介绍了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 查看字节码,我们会看到如下内容:

<前>公共类示例{公共示例();代码:0:aload_01: invokespecial #1//方法 java/lang/Object."<init>":()V4:返回public static void main(java.lang.String[]);代码:0: new #2//类 java/lang/StringBuilder3:重复4: invokespecial #3//方法 java/lang/StringBuilder."<init>":()V7:aload_08:iconst_09:加载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:aload_019:iconst_120:加载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:aload_030:iconst_231:加载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_139: getstatic #7//字段 java/lang/System.out:Ljava/io/PrintStream;42:aload_143: invokevirtual #8//方法 java/io/PrintStream.println:(Ljava/lang/String;)V46:返回}

如您所见,它创建了一个 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 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.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆