【问题标题】:Will things run quicker if I make my variables final?如果我将变量设为最终变量,事情会运行得更快吗?
【发布时间】:2011-03-08 06:17:15
【问题描述】:

我正在为 Android (Java) 编写代码。

我将 int 和 float 声明为正在进行的循环的一部分。

有些不需要在声明后更改。

如果我在声明时将它们都设置为final,事情会运行得更快吗?

[编辑]

谢谢大家。其实没想到会有什么改进,只是发现,浏览了各种大项目的源码后,还是比较常见的。干杯

【问题讨论】:

  • 试试吧。做一个愚蠢的速度测试:为所有类型(基元、字符串、基元的 WrapperClasses ......)调用 100000 次 getter。
  • 这不是一种可靠的测试方法。 VM 上的微基准测试很少。
  • 微基准测试是万恶之源
  • @Martijn:实际上,您基本上是在对 VM 进行基准测试,而不是代码。另见stackoverflow.com/questions/2842695/what-is-microbenchmarking

标签: java android


【解决方案1】:

事情不会运行得更快。 final 关键字只是编译时语法糖。


如果它实际上是static final,那么您可以利用编译时计算和任何引用中的值内联。因此,例如:

private static final long ONE_WEEK_IN_MILLIS = 7 * 24 * 60 * 60 * 1000L;

public void foo(Date date) {
    if (date.getTiem() > System.currentTimeMillis() + ONE_WEEK_IN_MILLIS) {
        // No idea what to do here?
    }
}

编译器会优化一个和另一个,最终结果如下:

private static final long ONE_WEEK_IN_MILLIS = 604800000L;

public void foo(Date date) {
    if (date.getTiem() > System.currentTimeMillis() + 604800000L) {
        // No idea what to do here?
    }
}

如果你运行反编译器,你会自己看到它。

【讨论】:

  • Reflection 是我最喜欢的 API。 private final Field field;,你说? ~reflection magic~ 不再! 我也喜欢 Javassist 用于 final 类,出于同样的原因,没有什么比向 java.lang.* 类注入新方法更有趣了:D
  • @BalusC:“事情不会运行得更快。” ——这通常是正确的,但有一些带有 Android 的 Java 实现的 SomeReallyFunkyStuff 可能会使这成为一个例外。不知何故。
  • 如何覆盖 final 关键字?我刚刚尝试过,但收到了 java.lang.IllegalAccessException: Can not set static final java.lang.String field Main$Foo.BAR to java.lang.String 从一个名为 throwFinalFieldIllegalAccessException 的方法抛出。
  • @sfussenegger: static final (compiletime constant) 与 final 非常不同。
  • 啊,我想我记得了。最终实例变量可以使用反射来设置。不过,对它们的访问通常是内联的。因此,field.get(obj)foo.field 返回不同的结果。时髦的东西:)
【解决方案2】:

虽然设置为 final 可能会影响速度,但每个 VM 或设备的答案很可能会有所不同。

不过,将它们声明为 final 并没有什么坏处,甚至可以称之为良好的编程风格。

至于性能,这看起来几乎肯定是过早的优化。剖析、发现瓶颈、重新思考您的算法。不要仅仅因为性能而在“final”上浪费时间——它几乎不能解决任何问题。

【讨论】:

  • 人们喜欢说最好在出现问题之前忽略性能。关于现有代码,我完全同意。但通常从一开始就做事而不浪费任何时间进行优化不会有什么坏处。
  • @sfussenegger 在理论上看起来很棒,但很多时候它也是一个可怕的想法。我尝试过的几个项目是不可读的,并且是调试和维护的噩梦。我花在维护它们上的时间比我第一次进行优化所节省的时间要多得多。
【解决方案3】:

如果您也将其设为静态(类变量),它可以提高性能,并且将 final 用于您知道不会改变的变量也是一种很好的编程习惯。虽然,您可能不希望它成为类变量,在这种情况下,我不确定是否可以提高性能,但我认为在很多情况下可能会。

http://docs.sun.com/app/docs/doc/819-3681/6n5srlhjs?a=view

当您将常量声明为静态最终变量时,动态编译器可以轻松地执行一些常量折叠优化。

如果方法参数未在方法中修改,则将其声明为 final。一般来说,如果所有变量在初始化或设置为某个值后没有被修改,则将它们声明为final。

因此,例如,如果您的代码将两个最终变量相乘,则在运行时,VM 可能会使用通常的睡眠/停机时间来计算该乘法的结果,因此它不必这样做在繁忙时期。

【讨论】:

  • 注意:static final (compiletime constants) 与final 非常不同。
  • 是的,我编辑了我的帖子 :) 但是如果循环使用静态最终变量,则没有内在问题。
  • final 变量也可以在方法范围内声明(我知道 OP 正在谈论这个)。他们不能是static final
  • 哦,是的,我明白了,他正在循环声明它们。好吧,我认为如果使用 final,它在许多情况下仍然会提高性能。我在同一页面上添加了另一个报价。我猜虚拟机可能会找到一个合适的时间来执行持续折叠优化,即使在运行时也是如此。
  • final 对局部变量(包括参数)仅在编译时存在。它在类文件中不存在,因此在运行时无论如何都无法提供帮助。
【解决方案4】:

我认为将变量设置为 final 是一种很好的做法(您可以使用 Eclipse 的 Preferences > Java > Code Style > Clean Up 来做到这一点)。虽然性能实际上可能会有所提高,但我希望这些差异可以忽略不计。在我看来,它有助于提高代码的可读性(即无需寻找分配),这当然是一件好事(tm)。

【讨论】:

  • 想解释一下为什么性能可能会提高?是否有一些依赖于“最终”属性的优化?可以在编译时检查最终属性是否存在违规行为,因此如果编译器无论如何都无法进行这些优化,我会感到惊讶......
  • 编译器可能会在一个狭窄的范围内进行优化,但“final”是一个很好的明确提示。但是由于编译器或 VM 最好被视为一个黑盒,因此没有理由不给它一个提示,更不用说假设它做了一些优化。
  • 由于它可以决定编译时间是否将“final”放在变量前面是否有效,它可以尝试将final放在每个变量前面并如果合法,无论如何都要进行这些优化,对吗?
  • 由于不能安全地假定公共静态和公共实例变量是最终的,编译器将永远无法假定它们是最终的。如果您将反射带入混合中,则没有实例或静态变量可以。但我说它“可能”会改善。由于我认为潜在的性能差异可以忽略不计,因此我并不关心它是否存在。我喜欢可读性的提高,这对我来说更重要。
  • @aioobe:这是我从任何人那里看到的最好的论点之一。
【解决方案5】:

当我们声明任何变量 final 时,意味着在编译时它会被识别,并且在运行应用程序时 JVM 不会检查它是否有任何操作,因为它被声明为 final(constant)。所以我们肯定会从 JVM 中移除开销。

所以我们可以说它会提高性能,如果取决于你的情况,变量是恒定的,让它成为最终的 如果你做 if static final 会更好.....

它们由 JVM 优化并保存在具有类文件“http://negev.wordpress.com/java-memory-brief/”的常量池中

【讨论】:

  • JVM 在运行时不检查。你有没有关于这个的RuntimeException
  • 这意味着,JVM 不会像任何非 final 字段那样包含该 final 字段的值,它们被 JVM 优化保存在带有 Classfile "negev.wordpress.com/java-memory-brief" 的常量池中
猜你喜欢
  • 1970-01-01
  • 2019-09-17
  • 2017-11-12
  • 1970-01-01
  • 2016-09-22
  • 1970-01-01
  • 2011-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多