Java在for循环中使用getter还是创建局部变量?

时间:2016-04-11 18:39:14

标签: java performance loops for-loop getter

我有一个运行4096次的for循环,它应该尽可能快。性能在这里非常重要。目前我在循环中使用getter方法,只返回循环过程中不会改变的字段中的值或对象。

示例:

for (;;) {
    doSomething(example.getValue());
}

使用getter是否有任何开销?使用以下方式更快吗?

示例:

Object object = example.getValue();
for (;;) {
    doSomething(object);
}

如果是,那么访问example.value等公共字段是否也是如此?

编辑:我不在循环中使用System.out.println()

修改:某些字段不是final。没有字段是volatile,没有方法(getter)是synchronized

5 个答案:

答案 0 :(得分:7)

作为Rogério answered,在循环(Object object = example.getValue();)之外获取对象引用可能比在循环内调用getter更快(或者至少永远不会更慢),因为

  • 在“最糟糕”的情况下,example.getValue()实际上可能会在后台执行一些非常耗费计算的东西,尽管getter methods应该是“微不足道的”。通过分配一次引用并重新使用它,您只需执行一次这种昂贵的计算。
  • 在“最佳”情况下,example.getValue()做了一些微不足道的事情,例如return value;,所以在循环中分配它并不比JIT编译器inlines the code之后的循环外更昂贵

但是,更重要的是两者之间的语义差异及其在多线程环境中可能产生的影响:如果对象example的状态发生变化,导致example.getValue()返回对于不同对象的引用,有可能在每次迭代中,方法doSomething(Object object)将通过直接调用Object实际操作doSomething(example.getValue());的不同实例。另一方面,通过在循环外调用getter并设置对返回的实例(Object object = example.getValue();)的引用,doSomething(object);将在object n 次上运行用于 n 迭代。

这种语义差异可能导致多线程环境中的行为与单线程环境中的行为完全不同。此外,这不一定是实际的“内存中”多线程问题:如果example.getValue()取决于例如数据库/ HDD /网络资源,这些数据可能在循环执行期间发生变化,即使Java应用程序本身是单线程的,也可能返回不同的对象。出于这个原因,最好考虑你实际想要用循环完成什么,然后选择最能反映预期行为的选项。

答案 1 :(得分:5)

这取决于吸气剂。

如果它是一个简单的吸气剂,JIT会将其内联到直接现场接入,因此不会产生可衡量的差异。从风格的角度来看,使用getter - 它的代码较少。

如果getter正在访问volatile字段,那么会有一个额外的内存访问命中,因为该值不能保存在寄存器中,但命中非常小。

如果getter是synchronized,那么使用局部变量的速度会快得多,因为每次调用都不需要获取和释放锁,但循环代码将使用可能过时的值。吸气者被召唤时的场地。

答案 2 :(得分:4)

您应该更喜欢循环外的局部变量,原因如下:

  1. 通过在单行代码中避免嵌套方法调用(如doSomething(example.getValue())),并允许代码为值提供更好,更具体的名称,使代码更容易阅读/理解通过getter方法返回。
  2. 并非所有的getter方法都是微不足道的(也就是说,它们有时会做一些可能很昂贵的工作),但是开发人员通常不会注意到它,假设给定的方法很简单,而且实际上并不便宜。在这种情况下,代码可能会在没有开发人员意识到的情况下受到重大性能影响。提取到局部变量往往会避免这个问题。

答案 3 :(得分:1)

很容易担心性能比必要的要多得多。我明白这感受。有些事情需要考虑:

  1. 4096并不多,所以除非必须在极短的时间内完成,否则不要担心这么多的表现。
  2. 如果在这个循环中还有其他任何远程昂贵的东西,那么吸气剂就不重要了。
  3. 过早优化是万恶之源。专注于使您的代码正确和清晰。然后测量并分析它并缩小最昂贵的东西,并处理它。如果可能,改进实际算法。
  4. 关于你的问题,我并不确切知道JIT的作用,但除非它能够肯定地证明example.getValue()example.value在循环中没有变化(除非字段是final并且getter是微不足道的,否则很难做到这一点然后逻辑上没有办法避免在前一个样本中反复调用getter,因为这样可能会改变程序的行为。重复的电话肯定是一些非零的额外工作。

    说了这么多,在循环之外创建局部变量,无论它是否更快,因为它更清晰。也许这会给你带来惊喜,但好的代码并不总是最短的。表达意图和其他信息非常重要。在这种情况下,循环外部的局部变量使得阅读代码的任何人都明白doSomething的参数没有变化(特别是如果你使它成为最终的),这对于知道是有用的。否则,他们可能需要进行一些额外的挖掘,以确保他们知道程序的行为。

答案 4 :(得分:0)

如果您需要尽快运行,则不应在关键部分使用System.out.println

关于吸气剂:使用吸气剂有轻微的开销,但你不应该为此烦恼。 Java在JIT编译器中确实有getter和setter优化。所以最终他们将被原生代码所取代。