即使认为这不是它的主要目的,我一直认为final
关键字(在某些情况下和 VM 实现中)可以帮助 JIT。
这可能是一个都市传说,但我从未想过设置一个场地final
会对表演产生负面影响。
直到我遇到这样的代码:
private static final int THRESHOLD = 10_000_000;
private static int [] myArray = new int [THRESHOLD];
public static void main(String... args) {
final long begin = System.currentTimeMillis();
//Playing with myArray
int index1,index2;
for(index1 = THRESHOLD - 1; index1 > 1; index1--)
myArray[index1] = 42; //Array initial data
for(index1 = THRESHOLD - 1; index1 > 1; index1--) {
//Filling the array
for(index2 = index1 << 1; index2 < THRESHOLD; index2 += index1)
myArray[index2] += 32;
}
long result = 0;
for(index1 = THRESHOLD - 1; index1 > 1; index1-=100)
result += myArray[index1];
//Stop playing, let's see how long it took
System.out.println(result);
System.out.println((System.currentTimeMillis())-begin+"ms");
}
让我们看一下:
private static int [] myArray = new int [THRESHOLD];
在 W7 64 位下,在连续运行 10 次的基础上,我得到以下结果:
THRESHOLD = 10^7
, 1.7.0u09 客户端虚拟机 (Oracle):myArray
当不是最终结果时,运行时间约为 2133 毫秒。- 最终运行时间约为 2287 毫秒
myArray
。 - -server VM 产生类似的数字,即 2131ms 和 2284ms。
THRESHOLD = 3x10^7
, 1.7.0u09 客户端虚拟机 (Oracle):myArray
当不是最终结果时,运行时间约为 7647 毫秒。- 最终运行时间约为 8190 毫秒
myArray
。 - -server VM 产生 ~7653ms 和 ~8150ms。
THRESHOLD = 3x10^7
, 1.7.0u01 客户端虚拟机 (Oracle):myArray
当不是最终状态时,运行时间约为 8166 毫秒。- 最终运行时间约为 9694 毫秒
myArray
。相差15%以上! - -server VM 对非最终版本产生了可忽略的差异,大约 1%。
备注:我使用 JDK 1.7.0u09 的 javac 生成的字节码进行所有测试。两个版本生成的字节码完全相同,除了myArray
声明,这是预期的。
那么为什么版本static final myArray
比带有慢的版本static myArray
呢?
编辑(使用我的代码片段的 Aubin 版本):
似乎带关键字的版本final
和不带关键字的版本之间的差异仅在于第一次迭代。不知何故,第一次迭代的版本final
总是比没有的版本慢,然后下一次迭代的时间相似。
例如,THRESHOLD = 10^8
使用 1.7.0u09 客户端并运行第一个计算大约需要 35 秒,而第二个“仅”需要 30 秒。
显然,VM 执行了优化,是 JIT 在起作用吗?为什么它不早点启动(例如通过编译嵌套循环的第二级,这部分是热点)?
请注意,我的评论对于 1.7.0u01 客户端 VM 仍然有效。使用那个版本(可能还有更早的版本),代码final myArray
运行速度比没有这个关键字的代码慢:2671ms vs 2331ms,基于 200 次迭代。