在初始化对象或将其保存为变量并在其上调用方法时,调用方法是否更快

时间:2013-07-02 12:40:58

标签: java object

假设有一个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)或....

7 个答案:

答案 0 :(得分: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预热之后,这些指令将被优化(消除),并且它将没有任何区别。

答案 1 :(得分:10)

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

在第一种情况下,由于您具有对象的引用,因此您也可以稍后访问该对象及其成员。因此,它不符合垃圾收集 [直到它超出范围] (此声明,如下面的评论所述,我仍然感到困惑)一旦我得到幕后概念,就会确认这个,或者没有更多的参考创建到该对象,使用引用赋值或任何其他方法。


顺便说一句,在你的第二个案例中,你忘了括号。它应该是:

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

答案 2 :(得分: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  

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

答案 3 :(得分:3)

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

答案 4 :(得分:1)

性能差异不大。

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

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

public static double getValue(HogeHoge hogehoge) {
    // some impl
}

答案 5 :(得分:1)

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

我选择的解决方案可以让您更容易理解最新情况,即使它需要再分配一个参考资料。

原则上,没有中间变量的版本当然应该保存引用所需的内存,这可以忽略不计,并且一旦执行操作而不是引用时它应该使垃圾收集器的对象可用超出了范围。

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

答案 6 :(得分:1)

有趣的事实(有点OT):在.NET之外,一个对象实际上可以在超出范围之前进行垃圾收集 - 即使其中一个方法仍然在执行(参见下面的示例代码或现场演示:{{ 3}} - 在Release配置下和调试器外部运行时打印出“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);
    }
}