Java如何决定数学表达式中的哪个运算符必须(un)装箱?

wha*_*Fox 5 java optimization autoboxing

我目前正在研究如何编写有效的Java代码的学士论文.以下四个代码片段是JMH基准测试的一部分,每个方法将执行每个方法100万次.

public final static int primitiveOnly(int dummy, int add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
public final static int primitiveToWrapper(int dummy, int add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
public final static int wrapperToPrimitive(Integer dummy, Integer add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
public final static Integer wrapperToWrapper(Integer dummy, Integer add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
Run Code Online (Sandbox Code Playgroud)

这四种方法的结果是:

  • originalOnly:1.7 ms /操作
  • primitiveToWrapper:2.2 ms /操作
  • wrapperToPrimitive:47.5毫秒/操作
  • wrapperToWrapper:48.2毫秒/操作

这种行为的原因在于,在primitiveToWrapper的操作期间,Integer值只需要取消装箱,其中wrapperToPrimitive中的操作必须将第一个操作数装入Integer,这会导致昂贵的对象创建.

Java有这样的特定原因吗?我阅读了The Java Language Specification,但未能找到这个问题的答案.

更新:

为了解决关于返回值的问题(感谢Phil Anderson)我更新了我的代码.另外,我将基准类中的所有Integer变量更改为int.这是新版本:

public final static int primitiveOnly(int dummy, int add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static int primitiveToWrapperIntDummy(int dummy, int add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static Integer primitiveToWrapperIntegerDummy(Integer dummy, int add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static int wrapperToPrimitiveIntDummy(int dummy, Integer add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static Integer wrapperToPrimitiveIntegerDummy(Integer dummy, Integer add1, int add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static int wrapperToWrapperIntDummy(int dummy, Integer add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}

public final static Integer wrapperToWrapperIntegerDummy(Integer dummy, Integer add1, Integer add2) {
    for(int i = 0; i < 10; i++) {
        dummy += (add1 + add2);
    }
    return dummy;
}
Run Code Online (Sandbox Code Playgroud)

结果是10次迭代的平均值(1次迭代=上述每种方法的100万次执行).

  • originalOnly:0.783
  • primitiveToWrapper(int dummy):0.735
  • primitiveToWrapper(Integer dummy):24.999
  • WrapperToPrimitive(int dummy):0.709
  • WrapperToPrimitive(Integer dummy):26.782
  • WrapperToWrapper(int dummy):0.764
  • WrapperToWrapper(Integer dummy):27.301

最终结果现在感觉更加直观.谢谢大家帮助我.

Phi*_*son 3

在代码的第二位中,每次为虚拟 java 赋值时都必须将其装箱为整数,因为这是变量的类型。它不知道您实际上从未调用过它的任何方法,并且它可能是一个简单的 int。

因此,每次遇到代码时,dummy += (add1 + add2);它都必须执行以下操作。

  • 拆箱假人
  • 执行添加操作
  • 将结果装回虚拟变量中

每次通过 for 循环都会执行此操作。