在Java中,将方法参数声明为final是否有性能原因?

120

在Java中,将方法参数声明为final是否有性能优化的目的?

例如:

public void foo(int bar) { ... }

对比:

public void foo(final int bar) { ... }

假设在foo()函数中,bar只会被读取,而不会被修改。


我想不出编译器为什么会在意你是否将方法参数声明为final。但是这个问题的真正答案是 - 编写两个函数,一个带有final参数,另一个带有常规参数。每个函数运行一百万次,看看是否有任何明显的运行时差异。如果您担心性能问题,非常重要的是对代码进行一些分析工作,找出确切地使您的代码变慢的原因。它几乎肯定不是您所期望的 :) - Mike Blandford
1
我建议您不要编写微型基准测试。您不知道 JIT 可以在何时进行哪些优化,而且仅通过“简单测试用例”对其行为的理解很可能是错误的。 - Edmondo
可能是这样,但在这里没有人编写或建议微基准测试... - Kip
1
@Mike Blandford的回答暗示使用微基准测试,是吗? - Ben Page
编写Java微基准测试是非常棘手的事情。 - Edmondo
5个回答

100

在本地变量和参数的类文件中不会出现final关键字,因此它不会影响运行时性能。它的唯一用途是澄清编码人员的意图,即变量不可更改(许多人认为这是使用它的可疑原因),以及处理匿名内部类。

关于方法本身的final修饰符是否具有性能优化的争论很多,因为在运行时,无论修饰符如何,这些方法都将被优化编译器内联。在这种情况下,它应该仅用于限制方法的重载。


6
虽然你可能认为 final 变量/参数可以像循环变量一样进行优化,但是一个好的编译器/运行时应该能够在没有 final 的情况下找到这个优化。 - John Gardner
10
我曾见过Sun的编译器在仅有的两个方法之间唯一的区别是局部变量的“finality”时,所生成的字节码稍微更短。微观优化确实存在,并且编译器也会进行此类优化。当然,真正重要的是JIT对字节码的处理方式,而当局部引用被编译成字节码时,它们失去了“finality”。我认为这就是为什么有关final局部变量有如此多的猜测:结果是相当不确定的。然而,使用final局部变量可以影响字节码——就它的价值而言。 - Christopher Schultz
由于它是非确定性的,因此实际上没有任何依赖优化的方法。当然,自2008年原始答案以来,VM实现可能已经发生了很多变化;-) - Robin

15

仅有使用 final 参数的好处在于,它可以用于匿名嵌套类。如果参数永远不会被更改,编译器将在其正常操作中检测到这一点,即使没有 final 修饰符也可以。参数被意外分配导致错误的情况非常罕见 - 如果您的方法足够大需要进行这个级别的工程处理,请将它们缩小 - 您调用的方法不能更改您的参数。


8
“参数被意外赋值导致错误的情况相当罕见。”这种情况比你想象的更普遍... - RAY
2
@RAY 你可能是对的,实际上我没有任何数据(除了我的个人经验)来支持那个说法。 - Dobes Vandermeer
更准确地说,@DobesVandermeer,这并不是对最终参数的“好处”。您所描述的只是任何本地变量(其中包括参数)必须在匿名嵌套类的本地范围内可见的所需语法。 - swooby

0

还有一个要注意的地方是,在方法内声明非 final 局部变量时——内部类实例可能会比堆栈帧存在时间更长,因此,当内部对象仍然存在时,局部变量可能已经消失。


-1

10
问题是关于在声明参数为final时,它不会对性能产生影响。 - Robin
在现代JIT(Hotspot)中,final关键字对参数或类应用都没有任何(可测量的)性能影响。 - kohlerm
2
你链接的这两篇文章都表明,final关键字更多是为开发人员提供语义标记,而JIT编译器可能会使用final(但正如其他人指出的那样,它们并不真正需要这些信息)。因此,final更多地是语义化的,这与现代C / C ++编译器能够推断变量和方法的const性质的能力相似,即使它们没有显式地标记为const。 - ron

-2

我认为编译器可能会删除所有具有原始类型(例如int)的私有静态常量变量,并像C++宏一样直接将它们内联到代码中。

然而,我不知道这是否在实践中完成,但为了节省一些内存,这是可以做到的。


1
问题不在于私有静态常量变量。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接