【问题标题】:Java String how many objects created in this snippet [duplicate]Java String 在此片段中创建了多少个对象[重复]
【发布时间】:2020-02-09 14:50:37
【问题描述】:

我正在阅读一些关于 Java 11 认证的书,这段代码真的引起了我的注意。

public class StringCreations {
    public static void main(String[] args) {
        String hello = "hello";/*A STRING CREATED HERE*/
        for(int i=0;i<5;i++){
            hello = hello + i;/*I THINK THAT A STRING IS CREATED IN EACH ITERATION.*/
        }
        System.out.println(hello);/*6 or 11 objects created at this time?? i think is 6*/
    }    
}

这本书说这个 sn-p 每次迭代创建了 11 个对象 2?这个对吗? 我认为每次迭代创建 1 个对象,总共创建 6 个对象。

【问题讨论】:

  • “书上说这个sn-p每次迭代创建了11个对象2个?” 11个objects还是11个strings?因为字符串连接是使用StringBuilders... 实现的
  • 查看字节码以了解实际情况
  • @HovercraftFullOfEels Java 11 中的字节码将只有一个 invokedynamic 进行连接,它(按设计)隐藏了字符串连接实现方式的所有细节。

标签: java string


【解决方案1】:

从根本上说,这不是一个真正有用的问题(我的意思是,这本书的问题,而不是你的问题),因为它涉及 Java 编译器和各种 JDK 方法的内部细节。但是……

这本书可能指的是 Java 8 或更早版本(尽管它是针对 Java 11 认证的——我猜他们没有更新这个示例)。在 Java 8 和更早版本中,该代码创建了六个字符串(嗯,其中一个 - 一开始分配给 hello 的那个 - 在加载类时创建,然后动态创建五个)。但它也会创建并丢弃StringBuilder 对象,每个循环迭代一个。由于有五次循环迭代,所以有五个StringBuilder 对象。

6 + 5 = 11。:-)

那是no longer true in Java 9 and above,谢天谢地。更多内容如下。

如果您编译类(使用 JDK 8 或更早版本),您可以看到 StringBuilders,然后使用 javap -c StringCreations 查看字节码的渲染:

公共静态无效主(java.lang.String[]); 代码: 0: ldc #2 // 字符串你好 2:astore_1 3:iconst_0 4:istore_2 5:iload_2 6:iconst_5 7: if_icmpge 35 10: new #3 // 类 java/lang/StringBuilder 13:重复 14: invokespecial #4 // 方法 java/lang/StringBuilder."":()V 17:加载_1 18: invokevirtual #5 // 方法 java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21:iload_2 22: invokevirtual #6 // 方法 java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 25: invokevirtual #7 // 方法 java/lang/StringBuilder.toString:()Ljava/lang/String; 28:astore_1 29: iinc 2, 1 32:转到 5 35: getstatic #8 // 字段 java/lang/System.out:Ljava/io/PrintStream; 38:加载_1 39: invokevirtual #9 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V 42:返回

循环的偏移量为 5 到 32。在偏移量 14 处,您可以看到正在创建一个 StringBuilder,然后在偏移量 25 处调用它的 toString(创建一个新字符串);循环。

第一个字符串并不是真正由该代码创建,它是通过加载类(以及它的常量池)创建的,但循环中的五个是,当然还有五个@ 987654331@s 在循环中。

将其与 Java 13 生成的字节码进行比较:

公共静态无效主(java.lang.String[]); 代码: 0: ldc #7 // 字符串你好 2:astore_1 3:iconst_0 4:istore_2 5:iload_2 6:iconst_5 7:if_icmpge 24 10:aload_1 11:iload_2 12: invokedynamic #9, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String; 17:astore_1 18: iinc 2, 1 21:转到 5 24: getstatic #13 // 字段 java/lang/System.out:Ljava/io/PrintStream; 27:加载_1 28: invokevirtual #19 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V

循环从偏移量 5 到偏移量 21,但不再看到 StringBuilders;相反,有人打电话给makeConcatWithConstants。所以你最终只得到了六个字符串(常量池中的一个,然后是通过makeConcatWithConstants 动态创建的五个)。

正如 kaya3 在评论中指出的那样,我们不知道(在这两种情况下)StringBuilder.appendmakeConcatWithConstants 是否在返回新字符串之前在其实现中将 i 转换为字符串。这意味着在 Java 8 中它将是 16 个对象(11 个字符串和 5 个StringBuilders),而在 Java 9+ 中则是 11 个字符串。但是鉴于makeConcatWithConstants 的重点是“......创建优化的字符串连接方法......”,我想我们可以假设它不会为i 创建一个字符串,而不是创建新的将成为其结果的字符串。但实际上,此时我们已经深入了解 Java 编译器、JVM 及其 JIT 等细节。

【讨论】:

  • 这就是我认为队友感谢委内瑞拉的问候。
  • Java 9+ 中的字符串连接不再编译为StringBuilder;见this question
  • @chiperortiz - 我的猜测是有人没有为字符串更改正确更新它。 :-) 他们可能只是添加了新材料,没有指出这个特定的例子已经过时了。
  • 是的,我发现了同样的方式(-:我认为问题对于 Java 9+ 来说并不那么简单,因为它是一个非常无证的实现细节,在运行时实际发生了什么。如果答案of 11 是最新的,并且没有对实现细节进行长时间的讨论,它可能将 i 转换为字符串(这可能会发生,但规范不保证),或者有动态调用字符串连接方法的一些有趣的事情,将可变参数作为需要实例化的数组(但我认为这不应该发生)。
  • 我认为你可能是对的,教科书只是有一个过时的问题。我认为这在 Java 11 中实际上是无法回答的。即使在 Java 8 中我也认为这是一团糟,因为 StringBuilder 在内部分配数组,并且在调用 append 时可能必须重新分配它们以增加容量;不应该计算数组吗?
猜你喜欢
  • 2014-11-26
  • 1970-01-01
  • 2018-04-28
  • 2019-08-22
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
  • 1970-01-01
  • 2014-08-01
相关资源
最近更新 更多