Java 当我初始化一个对象时调用一个方法,或者将它作为一个变量保存并调用该对象上的方法,速度更快吗

Java 当我初始化一个对象时调用一个方法,或者将它作为一个变量保存并调用该对象上的方法,速度更快吗,java,object,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(); 我知道

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

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

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

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


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

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

在性能方面没有太大差异

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

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

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

您可以自己查看字节码:

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  
0:dconst\u 0
1:dstore_1/*双值=0*
2:新#2//A类
5:dup
6:invokespecial#3//方法ClassA.“:()V
9:astore_3
10:aload_3
11:invokevirtual#4//方法ClassA.getValue:()D
14:dstore_1
15:新#2//A类
18:dup
19:invokespecial#3//方法ClassA.“:()V
22:invokevirtual#4//方法ClassA.getValue:()D
25:dstore_1
26:返回

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

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

我会选择更容易理解发生了什么的解决方案,即使它需要再分配一个引用

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


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

实际上,这两段代码会有一点不同:

***** 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
}
*****
但看起来,在jvm预热之后,这些指令将被优化(消除),并且不会有任何区别。

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

new ClassA(hogehoge).getValue;
是编译错误!请始终记住,编译器和虚拟机中进行了大量优化,因此在本例中几乎无法衡量任何差异。我理解。谢谢!应该注意的是,在大多数情况下,程序员(尤其是新手)都是如此根本不应该担心这种微优化。@AdamDyga:是的,你说得对,一般不关心微优化,但OP会问两者之间的区别是什么,哪一个更快,哪一个占用更多内存。谢谢你的回答。我很感激。我如何投票?@KensukeKonishi。不客气:)你可以吗p通过单击答案左侧的投票按钮记录答案。此外,如果答案有用,您可以通过单击投票按钮下方的箭头将其标记为已接受。这是不正确的。在第一种情况下,如果变量
a
,则对象有资格进行垃圾收集imm在调用
getValue
后立即执行。垃圾收集的合格性不取决于范围内变量的存在,而是取决于对象“是否可以在任何活动线程的任何潜在连续计算中访问”“.请参阅.+1,因为它提到了优化,这将不会导致任何差异。嗯,好的,我不熟悉jvm或编译器的优化…但无论如何,非常感谢你,我理解了。”
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);
    }
}