Java 中的自动装箱与手动装箱

2022-09-02 02:17:19

为什么第二段代码更快?

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));
    }
}

答案 1

自动装箱使用 ,它在内部缓存小整数的 Integer 对象(默认情况下为 -128 到 127,但可以使用 “java.lang.Integer.IntegerCache.high” 属性配置最大值 - 请参阅 Integer.valueOf 的源代码),因此它与直接调用不同。因为在调用之前快速检查整数值的大小,所以直接调用会快一点(尽管如果您有很多小整数,它会占用更多内存)。Java中的分配非常快,并且执行GC的时间与实时短命对象的数量成正比(即与垃圾量不成比例),因此GC也非常快。Integer.valueOfnew IntegerInteger.valueOfnew Integernew Integer

但是,根据 JVM 版本和启用的优化,存在标量替换优化,在分配短期对象时,这可能会产生更大的性能差异(在您的示例中,无法进行优化,因为您将对象存储在映射中,但在许多其他情况下,它很有用)。

在最近的JVM版本中,有标量替换优化(除了在1.6.0_18中,转义分析被暂时禁用),这意味着可以优化短期对象的分配。当JVM中的标量替换是新的时,有人做了一个基准测试,其中有类似于你的代码。结果是,使用基元的代码速度最快,具有显式调用的代码几乎与使用基元的代码一样快,而使用自动装箱的代码要慢得多。这是因为自动装箱使用,至少在当时,标量替换优化没有考虑到这种特殊情况。我不知道从那时起优化是否得到了改善。new Integer()Integer.valueOf


答案 2

自动装箱将使用 和 。调用这些方法有一些开销(尽管它最终会内联)。还会检查低值以使用池化实例,这在代码中通常不会获胜(尽管它可以稍微减小堆大小)。池化实例可能是一个胜利,它们可以减少堆大小,GC时间,甚至可以提高相等性测试性能。Integer.valueOfDouble.valueOfInteger.valueOf

但是,总的来说,这是一种微观优化,一般来说,你应该忽略它。


推荐