自动装箱的性能影响

问题描述 投票:13回答:3

通常,编译器会生成代码以执行装箱和拆箱。但是,如果不需要带框的值,编译器怎么办? (Oracle标准)编译器是否足够聪明,可以对其进行优化?

看看这个方法:

public static void requireInRange(int index, Object[] array) {
    if(index < 0 || index >= array.length)
        throw new IndexOutOfBoundsException();
}

唯一相关的信息是array.length,因此例如对数组的每个值进行装箱将是无用的。像这样的代码:

int[] anArray = {3, 4, 2};
requireInRange(3, anArray);

编译器将实际插入用于对数组的每个值进行装箱的代码吗?

java performance autoboxing
3个回答
41
投票

您的代码中没有自动装箱。实际上,给定:

public static void requireInRange(int index, Object[] array) {
   ...
}

int[] anArray = {3, 4, 2};
requireInRange(3, anArray); // DOES NOT COMPILE!!!

虽然int可以自动装箱到Integer,但是int[]确实NOT被Java自动装箱到Integer[]。您可以编写库函数来执行此操作,但是该语言将不会促进这种转换。

事实上,这是许多关于例如Arrays.asList(anIntArray)被“破坏”,因为返回的实际上不是一个元素List<Integer>,而是返回一个List<int[]>


但是性能如何呢?

[Java Language Guide/Autoboxing的引用:

将自动装箱和拆箱用于科学计算或其他对性能敏感的数字代码是不合适的。 Integer不能替代int。自动装箱和拆箱模糊了原始类型和引用类型之间的区别,但并不能消除它们。

简而言之,每当自动装箱发生时,性能肯定会受到影响。某些事情可以缓解这种情况,例如这些类型内置的缓存机制。这就是为什么您得到以下信息的原因:

    System.out.println(
        ((Integer) 0) == ((Integer) 0)
    );
    // true

    System.out.println(
        ((Integer) 10000) == ((Integer) 10000)
    );
    // false (implementation-specific)

这里发生的是,当0自动装箱时,实际上没有创建任何[[new Integer实例:出于自动装箱的目的,在一定范围内的值被cached,以提高性能。大多数实现中的10000可能不在此范围内,但是某些JVM实现的确允许您在必要时指定缓存范围。


但是我只想获取数组的长度!

有很多方法可以使您的requireInRange与任何类型的数组一起使用。不幸的是,使用Java的原语数组常常意味着很多重复。这意味着分别为int[]boolean[]byte[]Object[]等提供重载。

一个更简洁的选择是使用反射,但这有其优点和缺点。一般来说,对于大多数情况,反射不是首选的解决方案。

话虽这么说,java.lang.reflect.Array确实具有可以返回

ANY

数组长度的java.lang.reflect.Array int getLength(Object array)方法。它不是类型安全的(就像大多数反射机制一样);传递非数组编译,但在运行时抛出int getLength(Object array)相关问题

    [static-(IllegalArgumentException启发”)


4
投票
编译器将实际插入代码用于装箱数组的每个值?

编译器将拒绝代码,因为无法将Managing highly repetitive code and documentation in Java传递给采用java.util.Arrays参数的方法。

自动装箱仅对单个原始值发生,而不对整个数组发生。


3
投票
如果有疑问,您可以假定编译器未优化代码。通常,它会对代码进行字面翻译。

此外,如果有疑问,您可以假定JVM在运行时优化代码方面做得很好。除非您有充分的理由(例如探查器)怀疑这是一个问题,否则我不会认为这有什么区别。

© www.soinside.com 2019 - 2024. All rights reserved.