我正在对不同的数据结构进行基准测试时注意到,当我将变量声明为 final 时,性能提高了10-20%。
这真的让我很惊讶。我原本以为 final 关键字仅用于限制变量更改,并且优化会确定某个变量是否具有恒定值。
以下是示例:
import javafx.scene.input.KeyCode;
import java.util.*;
public class Main {
static /*final*/ int LOOPS = Integer.MAX_VALUE / 100;
static /*final*/ KeyCode[] keyCodes = KeyCode.values();
public static void main(String[] args) {
long startTime;
long endTime;
testEnumSet(); //warmup
startTime = System.nanoTime();
testEnumSet();
endTime = System.nanoTime();
System.out.println(" EnumSet: " + (endTime - startTime) + "ns");
}
static /*final*/ EnumSet<KeyCode> enumSet = EnumSet.noneOf(KeyCode.class);
static void testEnumSet() {
for (int i = 0; i < LOOPS; i++) {
/*final*/ KeyCode add = getRandomKeyCode();
if(!enumSet.contains(add)) enumSet.add(add);
/*final*/ KeyCode remove = getRandomKeyCode();
if(enumSet.contains(remove)) enumSet.remove(remove);
}
}
/*final*/ static Random random = new Random();
static KeyCode getRandomKeyCode() {
return keyCodes[random.nextInt(keyCodes.length)];
}
}
使用final关键字的代码:EnumSet: 652 266 207ns
不使用final关键字的代码:EnumSet: 802 121 596ns
这个差别一直都是可以重现的!为什么使用final关键字的代码执行速度如此之快,而没有使用final关键字的代码执行速度如此缓慢?为什么不会进行优化?另外,final关键字到底有何作用,以及在生成的字节码中有何不同呢?
main
中创建一个实例),效果可能会消失,因为静态字段参与常量折叠。然而,实例字段不是常量(从实例到实例变化),因此仅从冗余加载消除中受益,这仅依赖于非易失性而不是最终性。因此,如果您想对不可变对象进行基准测试,而不仅仅是全局常量,则您的基准测试无用。 - the8472LOOPS
被声明为final
时,它将成为一个编译时常量,这意味着它的值会被内联。换句话说,对于这个变量,添加final
修饰符实际上会改变使用它的代码。另一方面,对于局部变量,它的影响最小(甚至没有影响),因为类中不存在这些变量是否被声明为final
的信息。因此,final
的效果可能会有很大的差异。 - Holger