Java 使用最终局部变量的编译器优化
Java compiler optimizations with final local variables
我一直认为 final 关键字在性能方面对局部方法变量或参数没有影响。因此,我尝试测试以下代码,但似乎我错了:
private static String doStuffFinal() {
final String a = "A";
final String b = "B";
final int n = 2;
return a + b + n;
}
private static String doStuffNotFinal() {
String a = "A";
String b = "B";
int n = 2;
return a + b + n;
}
我检查了字节码,这两种方法并不相同。 idea中的反编译代码是这样的:
private static String doStuffFinal() {
String a = "A";
String b = "B";
int n = 2;
return "AB2";
}
private static String doStuffNotFinal() {
String a = "A";
String b = "B";
int n = 2;
return a + b + n;
}
为什么这两种方法有区别? javac 不能优化这样一个微不足道的案例吗?编译器可以看到 a、b 和 n 在 doStuffNotFinal 中没有改变,并以相同的方式优化代码。为什么没有发生这种情况?
更重要的是,这是否意味着我们最好将 final 关键字放在各处以确保获得最佳优化?
Why is there a difference between these 2 methods?
a
、b
和n
在doStuffFinal()
方法中是constant variables,因为:
A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.29)
但是 doStuffNotFinal
中的变量不是常量变量,因为它们不是最终的,因此它们的值不是常量表达式。
如Constant expressions所述,常量表达式操作数的二元运算符的结果也是常量表达式;所以a + b
是常量表达式,a + b + n
也是。还有:
Constant expressions of type String are always "interned"
所以a + b + n
是interned的,所以会出现在常量池中,反编译的时候会看到它的用处。
Can't javac optimize such a trivial case?
语言规范说常量字符串必须在最后一种情况下被驻留;它并没有说它不能在非最终情况下。所以,当然可以。
一天只有这么多时间;编译器实现者只能做这么多。这种微不足道的情况可能没有兴趣在编译器中进行优化,因为它会非常罕见。
does that mean we'd better put the final keyword all over the place
不要忘记 javac 并不是唯一进行优化的东西:javac 实际上非常愚蠢,并且在将 Java 代码转换为字节码时是字面意思。 JIT 中发生了更多有趣的优化。
此外,只有在非常特殊的情况下才能获得将事物设为 final 的好处:final String 或使用常量表达式初始化的原始变量。这当然取决于您的代码库,但这些不会占您变量的很大一部分。
因此,您当然可以 将它们喷洒到各处,但它提供的好处不可能超过让 final
分散在代码中的额外视觉噪音。
我一直认为 final 关键字在性能方面对局部方法变量或参数没有影响。因此,我尝试测试以下代码,但似乎我错了:
private static String doStuffFinal() {
final String a = "A";
final String b = "B";
final int n = 2;
return a + b + n;
}
private static String doStuffNotFinal() {
String a = "A";
String b = "B";
int n = 2;
return a + b + n;
}
我检查了字节码,这两种方法并不相同。 idea中的反编译代码是这样的:
private static String doStuffFinal() {
String a = "A";
String b = "B";
int n = 2;
return "AB2";
}
private static String doStuffNotFinal() {
String a = "A";
String b = "B";
int n = 2;
return a + b + n;
}
为什么这两种方法有区别? javac 不能优化这样一个微不足道的案例吗?编译器可以看到 a、b 和 n 在 doStuffNotFinal 中没有改变,并以相同的方式优化代码。为什么没有发生这种情况?
更重要的是,这是否意味着我们最好将 final 关键字放在各处以确保获得最佳优化?
Why is there a difference between these 2 methods?
a
、b
和n
在doStuffFinal()
方法中是constant variables,因为:
A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.29)
但是 doStuffNotFinal
中的变量不是常量变量,因为它们不是最终的,因此它们的值不是常量表达式。
如Constant expressions所述,常量表达式操作数的二元运算符的结果也是常量表达式;所以a + b
是常量表达式,a + b + n
也是。还有:
Constant expressions of type String are always "interned"
所以a + b + n
是interned的,所以会出现在常量池中,反编译的时候会看到它的用处。
Can't javac optimize such a trivial case?
语言规范说常量字符串必须在最后一种情况下被驻留;它并没有说它不能在非最终情况下。所以,当然可以。
一天只有这么多时间;编译器实现者只能做这么多。这种微不足道的情况可能没有兴趣在编译器中进行优化,因为它会非常罕见。
does that mean we'd better put the final keyword all over the place
不要忘记 javac 并不是唯一进行优化的东西:javac 实际上非常愚蠢,并且在将 Java 代码转换为字节码时是字面意思。 JIT 中发生了更多有趣的优化。
此外,只有在非常特殊的情况下才能获得将事物设为 final 的好处:final String 或使用常量表达式初始化的原始变量。这当然取决于您的代码库,但这些不会占您变量的很大一部分。
因此,您当然可以 将它们喷洒到各处,但它提供的好处不可能超过让 final
分散在代码中的额外视觉噪音。