今天在知乎上看到一个问题很有意思:
// 下面一小段程序, 修改注释处所指的常数, 并不影响函数调用次数和结果, 但运行时间差距很大.
// 常数=1时只需0.125秒; 常数=2时需要2.1秒; 常数=4时需要6.1秒.
// 测试环境是 OpenJDK 11.0.1 64-bit, Win10 64-bit
public class A {
int v;
public static void main(String[] args) {
A a = new A();
Runnable[] r = new Runnable[] {
() -> a.v++,
() -> a.v++,
() -> a.v++,
() -> a.v++,
};
for (int i = 0; i < 1000000000; i++)
r[i % 1].run(); // 这里的1改为2和4的三种情况性能有极大差距
System.exit(a.v);
}
}
自己运行了一下,结果与问题描述的时间基本一致,常数为1时i%1的结果恒为0,猜测jvm应该做了优化。
使用javap查看bytecode.发现除第59行使用不同参数外,无任何不同。
参数1使用,iconst_1,参数2使用iconst_2,参数4使用iconst_4,使用hsdis查看jit生成的汇编.发现jit进行了很多优化。
使用参数1的汇编被优化为类似
for (int i = 0; i < 1000000000; i++)
a.v++;
猜测被证实了,jvm确实很聪明。
使用参数2的汇编被优化为类似
for (int i = 0; i < 1000000000; i++)
switch(i){
case 0: A$Lambda$1();break;
case 1: A$Lambda$2();
}
这里A$Lambda函数也有优化。
使用参数4的应该是没有优化,所以性能最低。
宝剑锋从磨砺出,梅花香自苦寒来.