9

假设有一个 ClassA 类通过非静态方法给出我需要的值。

如果我只需要 ClassA 实例中的值,我想有两种可能的选择。

double value =0 ; // The value I actually need, the object is just transitory

1) ClassA a = new ClassA (hogehoge);
   value = a.getValue();

2) value = new ClassA(hogehoge).getValue();

我知道两者都可能有优势或劣势。但一般来说它们之间有什么区别?

在情况 2) 中,内存使用小于 1) 或....

4

7 回答 7

12

其实这两段代码会有一点不同:

***** Class1.p
       8: invokespecial #4                  // Method ClassA."<init>":(Ljava/lang/String;)V
      11: astore_3
      12: aload_3
      13: invokevirtual #5                  // Method ClassA.getValue:()D
      16: dstore_1
      17: dload_1
      18: dreturn
}
***** CLASS2.P
       8: invokespecial #4                  // Method ClassA."<init>":(Ljava/lang/String;)V
      11: invokevirtual #5                  // Method ClassA.getValue:()D
      14: dstore_1
      15: dload_1
      16: dreturn
}
*****

即,我们在这里看到变体 #1 的两个附加说明:

      11: astore_3
      12: aload_3

但是看起来,在 jvm 预热之后,这些指令将被优化(消除)并且根本没有区别。

于 2013-07-02T12:55:49.653 回答
10

唯一的区别是在第二种情况下,您创建的对象将在该语句之后立即有资格进行垃圾收集,因为您没有对该对象的任何引用。你在那里有一个未命名的对象。

在第一种情况下,由于您引用了一个对象,因此您稍后也可以访问该对象及其成员。所以,它没有资格进行垃圾收集[直到它超出范围](这个声明,正如下面的评论中所讨论的,我仍然很困惑。一旦我得到幕后概念,我会确认这一点) ,或者不再使用引用分配或任何其他方法创建对该对象的引用。


顺便说一句,在你的第二种情况下,你在那里忘记了一个括号。它应该是:

value = new ClassA(hogehoge).getValue();
于 2013-07-02T12:44:32.007 回答
4

您可以自己查看字节码:

double value = 0;
ClassA a = new ClassA();
value = a.getValue();
value = new ClassA().getValue();

变成

   0: dconst_0      
   1: dstore_1                          // *double value = 0;*
   2: new           #2                  // class ClassA
   5: dup           
   6: invokespecial #3                  // Method ClassA."<init>":()V
   9: astore_3      
  10: aload_3       
  11: invokevirtual #4                  // Method ClassA.getValue:()D
  14: dstore_1      
  15: new           #2                  // class ClassA
  18: dup           
  19: invokespecial #3                  // Method ClassA."<init>":()V
  22: invokevirtual #4                  // Method ClassA.getValue:()D
  25: dstore_1      
  26: return  

总的来说,这两种方法在速度方面没有真正的区别,你不应该担心这样的微优化。

于 2013-07-02T12:50:49.957 回答
3

差异可以忽略不计。第一种情况保持a对 ClassA 的引用(4 个字节),直到它被垃圾收集(例如,当您从您的方法返回时)。

于 2013-07-02T12:44:00.920 回答
1

性能方面差别不大。

主要区别在于,对于内联版本,创建的实例可在行执行完成后立即用于垃圾收集,并且可能完成之前,具体取决于 JVM。

尽管创建实例相当便宜,但请考虑使用静态方法 - 因此可以避免创建实例和垃圾收集:

public static double getValue(HogeHoge hogehoge) {
    // some impl
}
于 2013-07-02T12:45:29.487 回答
1

我的建议是不要关心这些微优化,而更多地关注源代码的可读性。

我会选择更容易理解正在发生的事情的解决方案,即使它需要分配更多参考。

原则上,没有中间变量的版本当然应该节省引用所需的内存,这可以忽略不计,并且它应该在操作执行后立即为垃圾收集器提供对象,而不是在引用结束时范围。

另外,请注意,由于编译器优化,这些代码片段可能会变成完全相同的字节码,这是您不应该关心它的另一个原因。

于 2013-07-02T12:54:42.580 回答
1

有趣的事实(有点 OT):在 .NET 下,一个对象实际上可以在超出范围之前被垃圾收集 - 即使它的一个方法仍在执行(参见下面的示例代码或现场演示:http://rextester .com/WTKSYE61526 - 在发布配置下和调试器外部运行时打印出“False False”)。

这告诉我们什么?
好吧 - JVM 的未来版本/不同实现可能会实现类似的行为(甚至可能有 JVM 已经这样做了)。

从理论上讲(假设是垃圾收集环境),这两个选项可以认为是相等的。由于 JVM 中的实现细节(如果它们没有像 Andremoniy 提到的那样进行优化),它们在执行期间可能只会有一点不同。实施细节可能会发生变化。

因此,不要为差异而烦恼,而是选择更具可读性的差异。

public class Program
{
    public static void Main(string[] args)
    {
        Program p = new Program();
        p.Foo();

        new Program().Foo();
    }

    public void Foo() 
    {
        WeakReference r = new WeakReference(this);

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        Console.WriteLine(r.IsAlive);
    }
}
于 2013-07-02T13:16:53.940 回答