太阳公司的HotSpot JIT编译器是否会自动将Java局部变量应用"final"关键字?

7
我听说过这个情况,但我无法找到一个明确的在线来源来证实它。
背景:一位同事喜欢将他的本地变量设为final。做这样的事情的原因之一是性能。我的观点是Java的HotSpot JIT编译器会自动检测不变的本地变量,并使它们成为final,所以我们自己这样做没有任何性能上的好处。
请注意,我是在询问将局部变量设为final是否是良好的编程实践,因为已经有足够多(与主题无关)的SO问题。 编辑:mrhobo提出了优化整数字面值的字节码的好点子。我应该举一个例子来说明我在提问时讨论的类型的代码:
Object doSomething(Foo foo) {
    if (foo == null) {
        return null;
    }

    final Bar bar = foo.getBar();
    final Baz baz = this.bazMap.get(bar);

    return new MyObject(bar, baz);
}

你认为在这种情况下,由于barbaz都被标记为final,同样的优化是否会发生?或者HotSpot自动检测到它们在方法范围内不会改变,并将它们视为final呢?

类似问题


5
"我的同事喜欢将他的局部变量声明为final。其中一个原因是为了提高性能。" => 这样做不会提高性能。所以讨论的余下部分是无关的!但对于字段来说情况有所不同:一个final字段和一个非final字段并不相同。 - assylias
1
@assylias "这不会提高性能。" ==> 参考资料? - Steve K
4
在你发布的链接中指出的事实是字节码相同,这是一个强有力的提示。但是,如果你仔细考虑一下,在Java内存模型中,final字段具有非常特定的语义,这使它们与非final字段不同,可能会影响性能。然而,局部变量是局部变量 - 我无法想象如何使其成为final会影响性能。 - assylias
你可能想要了解SSA,以了解为什么Java中局部变量的final(在C++中的const是一个不同的话题,因为如果传递到其他地方,局部变量可以更改其值)对于编译器来说完全没有优化意义。 - Voo
显示剩余2条评论
1个回答

7
为了理解为什么局部变量的final对编译器来说完全没有意义,了解编译器的工作原理是很有帮助的。基本上,没有编译器会直接操作源代码(或字节码),而是将其解析成某种中间表示形式(通常在应用优化时会使用几种不同的中间表示形式)。我所知道的几乎每个编译器都使用某种形式的静态单赋值(SSA)或短SSA形式作为其中间表示。
正如名称所示,SSA形式基本上意味着每个变量只被分配一次值。为了更清楚地说明这一点,假设我们有以下简单的代码片段:
y = 4
x = 5
y = 6
z = x + y

在SSA表达式中,它看起来像下面这样:
y_1 = 4
x_1 = 5
y_2 = 6
z_1 = y_2 + x_1

所以我们为什么要这样做呢?因为这样可以使许多编译器优化变得更加容易(例如,很容易看出对 y 的第一次写入可以被消除,因为我们从未读取过它)。因此,如果您愿意,您可以将其视为编译器中每个变量都已经是 final。

PS:循环和分支会使这变得有点复杂,但我不想在这里讨论得太多 - 维基百科上有一个简短的解释来说明如何解决这些问题。


谢谢@Voo。Java编译器是否对对象和字面量(如int)使用SSA形式? - Steve K
@Steve SSA表单使用变量而不是对象本身,这是一个重要的区别。所以,是的,它可以,这里指向对象的变量和包含原始数据的变量之间没有真正的区别。 - Voo

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