为什么传递两个字符串参数比传递一个列表参数更有效
下面的代码调用两个简单函数,每个函数调用 100 亿次。
public class PerfTest {
private static long l = 0;
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b");
long time1 = System.currentTimeMillis();
for (long i = 0; i < 1E10; i++) {
func1("a", "b");
}
long time2 = System.currentTimeMillis();
for (long i = 0; i < 1E10; i++) {
func2(list);
}
System.out.println((time2 - time1) + "/" + (System.currentTimeMillis() - time2));
}
private static void func1(String s1, String s2) { l++; }
private static void func2(List<String> sl) { l++; }
}
我的假设是,这两个调用的性能将接近相同。如果有的话,我会猜到传递两个参数会比传递一个参数稍微慢一些。鉴于所有参数都是对象引用,我没想到一个是列表的事实会有什么不同。
我已经多次运行测试,典型结果是“12781/30536”。换句话说,使用两个字符串的调用需要 13 秒,使用列表的调用需要 30 秒。
这种性能差异的原因是什么?还是这是一个不公平的测试?我尝试过切换两个调用(以防由于启动效果),但结果是相同的。
更新
由于许多原因,这不是一个公平的测试。但是,它确实展示了Java编译器的真实行为。请注意以下两个新增功能来演示这一点:
- 将表达式和添加到函数中会使两个函数调用性能相同
s1.getClass()
sl.getClass()
- 运行 测试 也使两个函数调用执行相同的
-XX:-TieredCompilation
这种行为的解释在下面接受的答案中。@apangin的答案的非常简短的总结是,热点编译器没有内联,因为它的参数(即)的类没有被解析。强制解析类(例如 using )会导致它被内联,从而显着提高其性能。正如答案中指出的那样,未解析的类不太可能出现在实际代码中,这使得此代码成为不切实际的边缘情况。func2
List
getClass