为什么在此循环中缓存的整数(-128到127)的操作比非缓存的操作慢?

时间:2016-02-23 18:44:41

标签: java performance

我有一个简单的程序:

public class Main {

    public static void main(String[] args) {
        long sum = 0L;
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            for (Integer j = 0; j < 100; j++) {
                sum++;
            }
        }
        System.out.println(System.currentTimeMillis() - start);
        System.out.println(sum);
    }
}

在我的机器上,这将在大约3300毫秒内执行。

当我将内循环中j变量的范围更改为:

for (Integer j = 1000; j < 1100; j++)

它在大约2500毫秒内执行。

我希望第二个版本的执行速度要慢得多,因为每次迭代都应该创建一个新的Integer。但它实际上更快。为什么呢?

Java版:

java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

1 个答案:

答案 0 :(得分:3)

  • 对象创建不是一个缓慢的操作。它在Java的早期曾经很慢,如果包含大型复杂对象的初始化时间,它可能仍然很慢。但是创建一个小的单场对象是一个非常快速的操作。
  • 尽管如此,建议不要创建大量对象仍然是相关的,因为它们会浪费内存。当内存不足时,垃圾收集器将运行并减慢程序的速度。
  • 当整数被装箱时,它会运行Integer.valueOf(int)。该方法的来源是:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    

    这意味着对于小数字,有:

    • 对象字段的两个比较
    • 否定
    • 除了
    • 在对象内部进行数组查找。

    对于大数字,有:

    • 对象字段的两个比较
    • 小对象分配
    • 初始化为零
    • 赋值。

    根据您的设置,对象分配可能会更快。在我的Mac上,这两个版本之间差别不大。在我的Linux上,我看到了与你描述的相同的结果。

  • 了解内存受到压力时的行为有多么不同。

    • 使用具有1000到1100值的版本。​​
    • 您可以对缓存和非缓存测试使用相同的程序:将-Djava.lang.Integer.IntegerCache.high=1500添加到java命令行,使缓存中包含范围1000-1100。
    • 要测试是否有内存压力,请添加-Xmx5M

我的Linux机器上的典型结果(Oracle Java 1.8):

$ java Main
2575
1000000000

$ java -Djava.lang.Integer.IntegerCache.high=1500 Main
3078
1000000000

$ java -Xmx5M Main
5812
1000000000

$ java -Xmx5M -Djava.lang.Integer.IntegerCache.high=1500 Main
3102
1000000000

结论:如果你分配了很多对象,当你拥有大量内存时,它可能会稍快一点,但是当你需要很少的内存并且需要垃圾收集时,它会明显变慢。