在Java中,通过getter引用字段与通过变量引用字段之间存在性能差异吗?

时间:2009-07-28 18:25:41

标签: java performance variables reference getter

执行

之间是否有任何差异
Field field = something.getSomethingElse().getField();
if (field == 0) {
//do something    
}
somelist.add(field);

if (something.getSomethingElse().getField() == 0) {
//do something    
}
somelist.add(something.getSomethingElse().getField());

通过getter对字段的引用会导致性能损失还是与引用已分配的变量相同?我理解变量只是对内存空间的引用,所以getter应该只是成为那个记忆空间的另一种方式。

请注意,这是一个学术问题(只是好奇的学校)而不是实用的问题。

9 个答案:

答案 0 :(得分:11)

这是一个微不足道的损害。不要太在乎自己,否则你会成为过早优化的牺牲品。如果你的申请很慢,这不是原因。

答案 1 :(得分:7)

假设getSomethingElse()被定义为

public SomethingElse getSomethingElse() {
    return this.somethingElse;
}

性能差异将是最小的(如果它被内联则为零)。然而,在现实生活中,你不能总是确定是这种情况 - 可能会在幕后发生一些处理(不一定在对象本身,但是,例如,通过AOP代理)。因此,将结果保存在变量中以便重复访问可能是一个好主意。

答案 2 :(得分:5)

通过getter访问变量会导致方法调用存在差异。在某些情况下,JVM可能会优化方法调用,但它方法调用。

也就是说,如果您的代码中最大的瓶颈或性能问题是来自访问器方法的开销,我会说您没有太多担心。

答案 3 :(得分:4)

存在性能损失(可能很小,可以忽略不计)但是,JVM可能会内联这些以及所有调用来提高性能。

如果你以第二种方式离开它会更好。

答案 4 :(得分:3)

如果你有一个好的JVM,就像Sun的HotSpot那样。它将内联并编译(到本机代码)getter。

使用getter通常是一种非常好的做法,作为一种防御措施,以及一般的信息隐藏。

答案 5 :(得分:2)

如果使用Java编写Android应用程序,请注意以下几点: http://developer.android.com/training/articles/perf-tips.html#GettersSetters

  

在像C ++这样的本地语言中,使用getter是常见的做法(i =   getCount())而不是直接访问该字段(i = mCount)。这个   是C ++的一个很好的习惯,并且经常在其他对象中实践   面向C#和Java的语言,因为编译器通常可以   内联访问,如果您需要限制或调试字段访问   您可以随时添加代码。

     

然而,这在Android上是一个糟糕的主意。虚拟方法调用是   比实例字段查找昂贵得多。这很合理   遵循常见的面向对象编程实践并拥有   公共界面中的getter和setter,但是在一个类中   应始终直接访问字段。

     

没有JIT,直接字段访问速度比调用a快3倍   琐碎的吸气剂。使用JIT(直接现场访问便宜)   访问本地),直接字段访问速度比快速快7倍   引用一个微不足道的吸气剂。

     

请注意,如果您使用的是ProGuard,则可以充分利用它们   世界,因为ProGuard可以为您内联访问器。

答案 6 :(得分:0)

如果该方法是一个简单的吸气剂,不涉及任何处理,则不是问题。如果它涉及大量计算,那么财产就无法做到你想做的事情。

我担心任何差异的唯一一次是在一个紧凑的循环中进行大量的迭代(数千次)。即便如此,如果您使用方面来编织额外的处理(例如日志记录),这可能只是一个问题,这可能涉及创建数千个额外的对象(例如JoinPoints和参数自动装箱)以及由此产生的GC问题。

答案 7 :(得分:0)

我不担心性能差异。你最好不要考虑它,而是花时间在一个真实的场景中分析你的代码。您很可能会发现程序的缓慢部分不在您认为的位置。

答案 8 :(得分:0)

这篇文章讨论的是CLI VM而不是JVM,但每个人都可以做类似的事情,所以我认为它是相关的。

我正在以一种特殊的方式为我的JIT处理这个特殊问题。请注意,此处的描述是概念性的,并且出于性能原因,代码以稍微不同的方式实现它。当我加载一个程序集时,如果它只返回一个成员字段,我会在方法描述符中做一个注释。当我稍后JIT其他方法时,我将所有call指令替换为字节代码中的这些方法,并使用ldfld指令,然后将其传递给本机代码生成器。通过这种方式,我可以:

  1. 在JIT中节省时间(ldfld对JIT的处理时间比call少。)
  2. 即使在baseline compiler
  3. 中内联属性
  4. 大体上保证使用公共属性/私有字段模式在分离调试器时不会产生任何类型的性能损失。 (当附加调试器时,我无法内联访问器。)
  5. 毫无疑问,虚拟机技术领域的知名人士已经在他们的产品中实现了类似(并且可能更好)的东西。