【问题标题】:How many objects will be created and how many objects will be available for garbage collection?将创建多少对象以及可用于垃圾回收的对象有多少?
【发布时间】:2016-11-08 05:50:33
【问题描述】:
String str = "test";

str = str + "test2";
str = str + "test3";
str = str + "test4";
str = str + "test5";

以上代码将创建多少对象,有多少对象可用于垃圾回收?

有人可以解释一下吗?

【问题讨论】:

标签: java string garbage-collection


【解决方案1】:

将创建多少个对象

在运行时,4,即str的四个计算值,不包括来自常量池的初始值。

还有多少对象可用于垃圾回收?

在这段代码的末尾但在str 超出范围之前,三个,即str 的三个中间值。

请注意,我计算的是Strings。每个String 都会有一个关联的char[],这是另一个对象。

但是,如果周围的代码使得 JVM 可以确定 str 不能在这些代码行之间更改,则它可能分别低至 1 和 0。

【讨论】:

  • 我认为String str = "test"; 也会在字符串常量池中创建一个对象,该对象位于JVM 的堆内存中。所以没有。对象数为 5。
  • @RohitGaikwad 但不是在运行时执行此代码。它发生在类加载时。
【解决方案2】:

JavaC 在字符串操作方面非常奇怪。例如,当您执行“String += otherString”时,他们为什么不使用 String.concat?

相反,Java 会为以 ; 结尾的每一行创建一个 StringBuilder(或 StringBuffer,取决于 Java 版本)。你有连接字符串。

我将您的代码放在一个测试程序 (TCTestWin) 中并从命令行调用:

javap -c TCTestWin.class

这些是结果:

   0: ldc           #15                 // String test
   2: astore_1
   3: new           #17                 // class java/lang/StringBuilder
   6: dup
   7: aload_1
   8: invokestatic  #19                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  11: invokespecial #25                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  14: ldc           #28                 // String test2
  16: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  19: invokevirtual #34                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  22: astore_1
  23: new           #17                 // class java/lang/StringBuilder
  26: dup
  27: aload_1
  28: invokestatic  #19                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  31: invokespecial #25                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  34: ldc           #38                 // String test3
  36: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  39: invokevirtual #34                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  42: astore_1
  43: new           #17                 // class java/lang/StringBuilder
  46: dup
  47: aload_1
  48: invokestatic  #19                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  51: invokespecial #25                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  54: ldc           #40                 // String test4
  56: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  59: invokevirtual #34                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  62: astore_1
  63: new           #17                 // class java/lang/StringBuilder
  66: dup
  67: aload_1
  68: invokestatic  #19                 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
  71: invokespecial #25                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
  74: ldc           #42                 // String test5
  76: invokevirtual #30                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  79: invokevirtual #34                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  82: astore_1
  83: return

如您所见,JavaC 为每一行创建一个 StringBuilder,附加字符串,然后继续。

这样的代码,特别是在循环中使用时,会冲向垃圾收集器。

有三种更好的方法:

  1. 使用 concat 代替:

    String str = "test";
    str = str.concat("test2");
    str = str.concat("test3");
    str = str.concat("test4");
    str = str.concat("test5");
    
  2. 使用单行连接,这将创建一个 StringBuilder。请记住,每个 ;将创建另一个 StringBuilder。请注意,在下面的常量字符串的串联中,Java 编译器将创建单个字符串。但是,如果添加一个或多个 String 变量,则会创建 StringBuilder。

    String str = "test" + "test2" + "test3" + "test4" + "test5";
    
  3. 自己创建一个StringBuilder,进行连接,然后重用 StringBuilder。这具有最佳性能,尤其是在循环执行操作时。

    StringBuilder sb = new StringBuilder(512);
    for (int i = 0; i < 10000; i++)
    {
       sb.setLength(0);
       sb.append("test");
       sb.append("test2");
       sb.append("test3");
       sb.append("test4");
       sb.append("test5");
       sb.append(i);
       String s = sb.toString();
    }
    

因此,从上面的代码中,将创建 4 个不同的 StringBuilder。在最终的String之后,所有的StringBuilders都会被收集起来。

【讨论】:

  • 如果使用编号或项目符号,以下代码需要双缩进。
  • 他们不使用多个String.concat() 调用,因为单个StringBuilder 的效率要高得多。编译器选择比程序员想象的更好的目标代码并没有什么“非常奇怪”的地方。这就是他们的目的。注意您实际上还没有回答这个问题。
  • 完全同意如果...创建了一个 StringBuilder。但是,发布的代码并没有创建单个 StringBuilder,而是 4 个!
  • 如果String str = "test" + "test2" + "test3" + "test4" + "test5" 创建了一个StringBuilder,我将扔掉编译器并得到一个不同的编译器。这不应创建任何StringBuilder。 Java 有规定右侧是常量表达式的规则(这在某些情况下会有所不同,因为有些地方只允许使用常量表达式)。因此,编译器应该已经知道它必须将其作为常量处理,因此,它应该创建 one 字符串 "testtest2test3test4test5".
  • @ajb,你是对的。添加 String 常量将使 java 在编译时创建单个 String。但是,在现实世界中,我相信他不会添加常量,而是添加不同的变量。我发送的代码将教他添加时在真实代码中会发生什么,而不是他发送的这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-20
相关资源
最近更新 更多