为什么第二段代码更快?
Map<Integer, Double> map = new HashMap<Integer, Double>();
for (int i = 0; i < 50000; i++) {
for (double j = 0.0; j < 10000; j++) {
map.put(i, j);
}
}
Map<Integer, Double> map=new HashMap<Integer, Double>();
for (int i = 0; i < 50000; i++) {
for (double j = 0.0; j < 10000; j++) {
map.put(new Integer(i), new Double(j));
}
}
答案 0 :(得分:47)
Autoboxing使用Integer.valueOf
,它在内部缓存小整数的Integer对象(默认为-128到127,但最大值可以使用“java.lang.Integer.IntegerCache.high”属性配置 - 请参阅Integer.valueOf的源代码,因此它与直接调用new Integer
不同。因为Integer.valueOf
在调用new Integer
之前会快速检查整数值的大小,所以直接调用new Integer
要快一点(尽管如果你有很多小的话,它会占用更多的内存)整数)。 Java中的分配非常快,GC的时间与活动短期对象的数量成正比(即与垃圾量不成比例),因此GC也非常快。
但是根据JVM版本和启用了哪些优化,存在标量替换优化,这可以在分配短期对象时产生更大的性能差异(在您的示例中,无法完成优化,因为你将对象存储在地图中,但在许多其他情况下它是有用的。)
在最近的JVM版本中,存在scalar replacement优化(除了1.6.0_18,其中转义分析为temporarily disabled),这意味着可以优化短期对象的分配。当JVM中的标量替换是新的时,有人使a benchmark的代码类似于你的代码。结果是使用原语的代码最快,具有显式new Integer()
调用的代码几乎与使用原语的代码一样快,并且使用自动装箱的代码要慢得多。这是因为自动装箱使用Integer.valueOf
,至少当时的标量替换优化没有考虑到这种特殊情况。我不知道从那以后优化是否得到了改善。
答案 1 :(得分:14)
自动装箱将使用Integer.valueOf
和Double.valueOf
。调用这些方法有一些开销(尽管它最终会被内联)。同样Integer.valueOf
会检查低值以使用池化实例,这在代码中并不常见(虽然它可以减少堆大小)。池化实例可能是一个胜利,它们可以减少堆大小,GC时间,甚至可以提高相等测试性能。
但是,总的来说,这是一种微观优化,你应该忽略它。
答案 2 :(得分:7)
因为微基准测试的结果不可靠?
此外,自动装箱是使用Integer.valueOf()和Double.valueOf()完成的,而不是构造函数。