Java 编译器是否优化了不必要的三元运算符?
Does the Java compiler optimize an unnecessary ternary operator?
我一直在审查一些编码人员使用冗余三元运算符“为了可读性”的代码。如:
boolean val = (foo == bar && foo1 != bar) ? true : false;
显然,将语句的结果分配给 boolean
变量会更好,但是编译器会关心吗?
是的,Java 编译器确实进行了优化。很容易验证:
public class Main1 {
public static boolean test(int foo, int bar, int baz) {
return foo == bar && bar == baz ? true : false;
}
}
在 javac Main1.java
和 javap -c Main1
之后:
public static boolean test(int, int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpne 14
10: iconst_1
11: goto 15
14: iconst_0
15: ireturn
public class Main2 {
public static boolean test(int foo, int bar, int baz) {
return foo == bar && bar == baz;
}
}
在 javac Main2.java
和 javap -c Main2
之后:
public static boolean test(int, int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpne 14
10: iconst_1
11: goto 15
14: iconst_0
15: ireturn
两个示例都以完全相同的字节码结尾。
在 IntelliJ 中,我编译了您的代码并打开了 class 文件,该文件会自动反编译。结果是:
boolean val = foo == bar && foo1 != bar;
所以是的,Java 编译器对其进行了优化。
我发现不必要地使用三元运算符会使代码更加混乱,可读性降低,这与初衷背道而驰。
也就是说,通过比较 JVM 编译的字节码,可以很容易地测试编译器在这方面的行为。
这里有两个 mock 类 来说明这一点:
情况一(没有三元运算符):
class Class {
public static void foo(int a, int b, int c) {
boolean val = (a == c && b != c);
System.out.println(val);
}
public static void main(String[] args) {
foo(1,2,3);
}
}
案例二(使用三元运算符):
class Class {
public static void foo(int a, int b, int c) {
boolean val = (a == c && b != c) ? true : false;
System.out.println(val);
}
public static void main(String[] args) {
foo(1,2,3);
}
}
案例 I 中 foo() 方法的字节码:
0: iload_0
1: iload_2
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpeq 14
10: iconst_1
11: goto 15
14: iconst_0
15: istore_3
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: iload_3
20: invokevirtual #3 // Method java/io/PrintStream.println:(Z)V
23: return
案例 II 中 foo() 方法的字节码:
0: iload_0
1: iload_2
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpeq 14
10: iconst_1
11: goto 15
14: iconst_0
15: istore_3
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: iload_3
20: invokevirtual #3 // Method java/io/PrintStream.println:(Z)V
23: return
请注意,在这两种情况下,字节码是相同的,即编译器在编译 val
布尔值时忽略三元运算符。
编辑:
关于这个问题的讨论已经转向了几个方向之一。
如上所示,在这两种情况下(有或没有冗余三元)编译的java字节码是相同的。
这是否可以被 Java 编译器视为 优化 在某种程度上取决于您对优化的定义。在某些方面,正如在其他答案中多次指出的那样,争论不是有道理的——这不是一种优化,因为在这两种情况下,生成的字节码都是执行的最简单的堆栈操作集这个任务,不管三元。
但是关于主要问题:
Obviously it would be better to just assign the statement’s result to
the boolean variable, but does the compiler care?
简单的答案是否定的。编译器不关心。
与的答案相反,
and 我认为编译器 不会优化掉(或忽略)三元运算符 。 (澄清:我指的是 Java 到字节码编译器,而不是 JIT)
查看测试用例。
Class 1:计算布尔表达式,将其存储在一个变量中,return那个变量.
public static boolean testCompiler(final int a, final int b)
{
final boolean c = ...;
return c;
}
因此,对于不同的布尔表达式,我们检查字节码:
1.表达式:a == b
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_2
11: iload_2
12: ireturn
- 表达式:
a == b ? true : false
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_2
11: iload_2
12: ireturn
- 表达式:
a == b ? false : true
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_0
6: goto 10
9: iconst_1
10: istore_2
11: iload_2
12: ireturn
案例 (1) 和 (2) 编译为完全相同的字节码,不是因为编译器优化掉了三元运算符,而是因为它本质上每次都需要执行那个简单的三元运算符。它需要在字节码级别指定 return 是真还是假。要验证这一点,请查看案例 (3)。除了交换第 5 行和第 9 行之外,它与字节码完全相同。
当反编译生成 a == b
时 a == b ? true : false
会发生什么?选择最简单的路径是反编译器的选择。
此外,根据"Class 1"实验,可以合理地假设a == b ? true : false
在转换为字节码的方式上与a == b
完全相同。然而,事实并非如此。为了测试我们检查以下 "Class 2",与 "Class 1" 的唯一区别是它不将布尔结果存储在变量中,而是立即 return 存储它。
Class 2:计算一个布尔表达式和return结果(不将其存储在变量)
public static boolean testCompiler(final int a, final int b)
{
return ...;
}
a == b
字节码:
0: iload_0
1: iload_1
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
a == b ? true : false
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
a == b ? false : true
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_0
6: goto 10
9: iconst_1
10: ireturn
这里很明显a == b
和a == b ? true : false
表达式的编译方式不同,如case( 1) 和 (2) 产生不同的字节码(如预期的那样,情况 (2) 和 (3) 仅交换了第 5,9 行)。
一开始我觉得这很奇怪,因为我原以为所有 3 个案例都是一样的(不包括案例 (3) 的交换行 5,9)。当编译器遇到 a == b
时,它会在遇到 a == b ? true : false
之后立即计算表达式和 returns,而在遇到 a == b ? true : false
时它会使用 goto
转到行 ireturn
.我知道这样做是为了留下 space 以便在三元运算符的 'true' 情况下评估潜在的语句:在 if_icmpne
检查和 goto
行之间。即使在这种情况下它只是一个布尔值 true
, 编译器也会像在一般情况下那样处理它,其中会出现更复杂的块.
另一方面,"Class 1" 实验掩盖了这一事实,因为在 true
分支中还有 istore
、iload
而不仅仅是 ireturn
迫使 goto
命令并在情况 (1) 和 (2) 中产生完全相同的字节码。
关于测试环境的说明,这些字节码是使用最新的 Eclipse (4.10) 生成的,它使用各自的 ECJ 编译器,不同于 IntelliJ IDEA 使用的 javac。
但是,阅读其他答案(使用 IntelliJ)中的 javac 生成的字节码,我相信同样的逻辑也适用于那里,至少对于 "Class 1" 实验,其中值已存储但未立即 returned。
最后,正如其他答案(例如 and 的答案)中已经指出的那样,在此线程和 SO 的其他问题中,大量优化是由 JIT 编译器完成的,而不是来自java-->java-字节码编译器,所以这些检查虽然对字节码翻译提供了信息,但并不是衡量最终优化代码将如何执行的好方法。
补充: 的回答比较了 javac 和 ECJ 针对类似情况生成的字节码
(作为免责声明,我没有研究 Java 编译或反汇编那么多以真正了解它在幕后做了什么;我的结论主要基于上述实验的结果。)
javac 编译器通常不会在输出字节码之前尝试优化代码。相反,它依赖于 Java 虚拟机 (JVM) 和即时 (JIT) 编译器,将字节码转换为机器码,从而使构造等同于更简单的构造。
这使得确定 Java 编译器的实现是否正常工作变得更加容易,因为大多数结构只能由一个预定义的字节码序列表示。如果编译器生成任何其他字节码序列,它就会被破坏,即使该序列的行为方式与原始序列相同。
检查 javac 编译器的字节码输出并不是判断构造是否可能有效执行的好方法。看起来可能存在某些 JVM 实现,其中 (someCondition ? true : false)
等构造的性能比 (someCondition)
差,而某些 JVM 实现的性能相同。
我想要 synthesize 之前答案中提供的极好的信息。
下面代码看看Oracle的javac和Eclipse的ecj做了什么:
boolean valReturn(int a, int b) { return a == b; }
boolean condReturn(int a, int b) { return a == b ? true : false; }
boolean ifReturn(int a, int b) { if (a == b) return true; else return false; }
void valVar(int a, int b) { boolean c = a == b; }
void condVar(int a, int b) { boolean c = a == b ? true : false; }
void ifVar(int a, int b) { boolean c; if (a == b) c = true; else c = false; }
(我稍微简化了您的代码 - 一次比较而不是两次比较 - 但下面描述的编译器的行为基本相同,包括它们略有不同的结果。)
我用javac和ecj编译代码,然后用Oracle的javap反编译。
这是 javac 的结果(我尝试了 javac 9.0.4 和 11.0.2 - 它们生成完全相同的代码):
boolean valReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
boolean condReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
boolean ifReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
void valVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void condVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void ifVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 10
5: iconst_1
6: istore_3
7: goto 12
10: iconst_0
11: istore_3
12: return
这是 ecj(版本 3.16.0)的结果:
boolean valReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
boolean condReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
boolean ifReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
void valVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void condVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void ifVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 10
5: iconst_1
6: istore_3
7: goto 12
10: iconst_0
11: istore_3
12: return
对于六个函数中的五个,两个编译器生成的代码完全相同。 The only difference 在 valReturn
中:javac 生成 goto
到 ireturn
,但 ecj 生成 ireturn
。对于 condReturn
,它们都生成 goto
到 ireturn
。对于 ifReturn
,它们都生成一个 ireturn
。
这是否意味着其中一个编译器优化了其中一种或多种情况?可能有人会认为javac优化了ifReturn
代码,但是没有优化valReturn
和condReturn
,而ecj优化了ifReturn
和valReturn
,但是没有优化condReturn
.
但我认为那不是真的。 Java 源代码编译器基本上根本不优化代码。 做优化代码的编译器是JIT(即时)编译器(JVM中将字节码编译成机器码的部分),JIT编译器可以做一个如果字节码相对简单,即未被优化,效果会更好。
简而言之:不,Java 源代码编译器不会优化这种情况,因为它们并没有真正优化任何东西。他们只做规范要求他们做的事,仅此而已。 javac 和 ecj 开发人员只是为这些情况选择了稍微不同的代码生成策略(大概是出于或多或少的任意原因)。
有关更多详细信息,请参阅 these Stack Overflow questions。
(恰当的例子:现在两个编译器都忽略了 -O
标志。ecj 选项明确这样说:-O: optimize for execution time (ignored)
。javac 甚至不再提及该标志,只是忽略它。)
我一直在审查一些编码人员使用冗余三元运算符“为了可读性”的代码。如:
boolean val = (foo == bar && foo1 != bar) ? true : false;
显然,将语句的结果分配给 boolean
变量会更好,但是编译器会关心吗?
是的,Java 编译器确实进行了优化。很容易验证:
public class Main1 {
public static boolean test(int foo, int bar, int baz) {
return foo == bar && bar == baz ? true : false;
}
}
在 javac Main1.java
和 javap -c Main1
之后:
public static boolean test(int, int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpne 14
10: iconst_1
11: goto 15
14: iconst_0
15: ireturn
public class Main2 {
public static boolean test(int foo, int bar, int baz) {
return foo == bar && bar == baz;
}
}
在 javac Main2.java
和 javap -c Main2
之后:
public static boolean test(int, int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpne 14
10: iconst_1
11: goto 15
14: iconst_0
15: ireturn
两个示例都以完全相同的字节码结尾。
在 IntelliJ 中,我编译了您的代码并打开了 class 文件,该文件会自动反编译。结果是:
boolean val = foo == bar && foo1 != bar;
所以是的,Java 编译器对其进行了优化。
我发现不必要地使用三元运算符会使代码更加混乱,可读性降低,这与初衷背道而驰。
也就是说,通过比较 JVM 编译的字节码,可以很容易地测试编译器在这方面的行为。
这里有两个 mock 类 来说明这一点:
情况一(没有三元运算符):
class Class {
public static void foo(int a, int b, int c) {
boolean val = (a == c && b != c);
System.out.println(val);
}
public static void main(String[] args) {
foo(1,2,3);
}
}
案例二(使用三元运算符):
class Class {
public static void foo(int a, int b, int c) {
boolean val = (a == c && b != c) ? true : false;
System.out.println(val);
}
public static void main(String[] args) {
foo(1,2,3);
}
}
案例 I 中 foo() 方法的字节码:
0: iload_0
1: iload_2
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpeq 14
10: iconst_1
11: goto 15
14: iconst_0
15: istore_3
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: iload_3
20: invokevirtual #3 // Method java/io/PrintStream.println:(Z)V
23: return
案例 II 中 foo() 方法的字节码:
0: iload_0
1: iload_2
2: if_icmpne 14
5: iload_1
6: iload_2
7: if_icmpeq 14
10: iconst_1
11: goto 15
14: iconst_0
15: istore_3
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: iload_3
20: invokevirtual #3 // Method java/io/PrintStream.println:(Z)V
23: return
请注意,在这两种情况下,字节码是相同的,即编译器在编译 val
布尔值时忽略三元运算符。
编辑:
关于这个问题的讨论已经转向了几个方向之一。
如上所示,在这两种情况下(有或没有冗余三元)编译的java字节码是相同的。
这是否可以被 Java 编译器视为 优化 在某种程度上取决于您对优化的定义。在某些方面,正如在其他答案中多次指出的那样,争论不是有道理的——这不是一种优化,因为在这两种情况下,生成的字节码都是执行的最简单的堆栈操作集这个任务,不管三元。
但是关于主要问题:
Obviously it would be better to just assign the statement’s result to the boolean variable, but does the compiler care?
简单的答案是否定的。编译器不关心。
与
查看测试用例。
Class 1:计算布尔表达式,将其存储在一个变量中,return那个变量.
public static boolean testCompiler(final int a, final int b)
{
final boolean c = ...;
return c;
}
因此,对于不同的布尔表达式,我们检查字节码:
1.表达式:a == b
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_2
11: iload_2
12: ireturn
- 表达式:
a == b ? true : false
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_2
11: iload_2
12: ireturn
- 表达式:
a == b ? false : true
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_0
6: goto 10
9: iconst_1
10: istore_2
11: iload_2
12: ireturn
案例 (1) 和 (2) 编译为完全相同的字节码,不是因为编译器优化掉了三元运算符,而是因为它本质上每次都需要执行那个简单的三元运算符。它需要在字节码级别指定 return 是真还是假。要验证这一点,请查看案例 (3)。除了交换第 5 行和第 9 行之外,它与字节码完全相同。
当反编译生成 a == b
时 a == b ? true : false
会发生什么?选择最简单的路径是反编译器的选择。
此外,根据"Class 1"实验,可以合理地假设a == b ? true : false
在转换为字节码的方式上与a == b
完全相同。然而,事实并非如此。为了测试我们检查以下 "Class 2",与 "Class 1" 的唯一区别是它不将布尔结果存储在变量中,而是立即 return 存储它。
Class 2:计算一个布尔表达式和return结果(不将其存储在变量)
public static boolean testCompiler(final int a, final int b)
{
return ...;
}
a == b
字节码:
0: iload_0
1: iload_1
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
a == b ? true : false
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
a == b ? false : true
字节码
0: iload_0
1: iload_1
2: if_icmpne 9
5: iconst_0
6: goto 10
9: iconst_1
10: ireturn
这里很明显a == b
和a == b ? true : false
表达式的编译方式不同,如case( 1) 和 (2) 产生不同的字节码(如预期的那样,情况 (2) 和 (3) 仅交换了第 5,9 行)。
一开始我觉得这很奇怪,因为我原以为所有 3 个案例都是一样的(不包括案例 (3) 的交换行 5,9)。当编译器遇到 a == b
时,它会在遇到 a == b ? true : false
之后立即计算表达式和 returns,而在遇到 a == b ? true : false
时它会使用 goto
转到行 ireturn
.我知道这样做是为了留下 space 以便在三元运算符的 'true' 情况下评估潜在的语句:在 if_icmpne
检查和 goto
行之间。即使在这种情况下它只是一个布尔值 true
, 编译器也会像在一般情况下那样处理它,其中会出现更复杂的块.
另一方面,"Class 1" 实验掩盖了这一事实,因为在 true
分支中还有 istore
、iload
而不仅仅是 ireturn
迫使 goto
命令并在情况 (1) 和 (2) 中产生完全相同的字节码。
关于测试环境的说明,这些字节码是使用最新的 Eclipse (4.10) 生成的,它使用各自的 ECJ 编译器,不同于 IntelliJ IDEA 使用的 javac。
但是,阅读其他答案(使用 IntelliJ)中的 javac 生成的字节码,我相信同样的逻辑也适用于那里,至少对于 "Class 1" 实验,其中值已存储但未立即 returned。
最后,正如其他答案(例如
补充:
(作为免责声明,我没有研究 Java 编译或反汇编那么多以真正了解它在幕后做了什么;我的结论主要基于上述实验的结果。)
javac 编译器通常不会在输出字节码之前尝试优化代码。相反,它依赖于 Java 虚拟机 (JVM) 和即时 (JIT) 编译器,将字节码转换为机器码,从而使构造等同于更简单的构造。
这使得确定 Java 编译器的实现是否正常工作变得更加容易,因为大多数结构只能由一个预定义的字节码序列表示。如果编译器生成任何其他字节码序列,它就会被破坏,即使该序列的行为方式与原始序列相同。
检查 javac 编译器的字节码输出并不是判断构造是否可能有效执行的好方法。看起来可能存在某些 JVM 实现,其中 (someCondition ? true : false)
等构造的性能比 (someCondition)
差,而某些 JVM 实现的性能相同。
我想要 synthesize 之前答案中提供的极好的信息。
下面代码看看Oracle的javac和Eclipse的ecj做了什么:
boolean valReturn(int a, int b) { return a == b; }
boolean condReturn(int a, int b) { return a == b ? true : false; }
boolean ifReturn(int a, int b) { if (a == b) return true; else return false; }
void valVar(int a, int b) { boolean c = a == b; }
void condVar(int a, int b) { boolean c = a == b ? true : false; }
void ifVar(int a, int b) { boolean c; if (a == b) c = true; else c = false; }
(我稍微简化了您的代码 - 一次比较而不是两次比较 - 但下面描述的编译器的行为基本相同,包括它们略有不同的结果。)
我用javac和ecj编译代码,然后用Oracle的javap反编译。
这是 javac 的结果(我尝试了 javac 9.0.4 和 11.0.2 - 它们生成完全相同的代码):
boolean valReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
boolean condReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
boolean ifReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
void valVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void condVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void ifVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 10
5: iconst_1
6: istore_3
7: goto 12
10: iconst_0
11: istore_3
12: return
这是 ecj(版本 3.16.0)的结果:
boolean valReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
boolean condReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: ireturn
boolean ifReturn(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 7
5: iconst_1
6: ireturn
7: iconst_0
8: ireturn
void valVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void condVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 9
5: iconst_1
6: goto 10
9: iconst_0
10: istore_3
11: return
void ifVar(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmpne 10
5: iconst_1
6: istore_3
7: goto 12
10: iconst_0
11: istore_3
12: return
对于六个函数中的五个,两个编译器生成的代码完全相同。 The only difference 在 valReturn
中:javac 生成 goto
到 ireturn
,但 ecj 生成 ireturn
。对于 condReturn
,它们都生成 goto
到 ireturn
。对于 ifReturn
,它们都生成一个 ireturn
。
这是否意味着其中一个编译器优化了其中一种或多种情况?可能有人会认为javac优化了ifReturn
代码,但是没有优化valReturn
和condReturn
,而ecj优化了ifReturn
和valReturn
,但是没有优化condReturn
.
但我认为那不是真的。 Java 源代码编译器基本上根本不优化代码。 做优化代码的编译器是JIT(即时)编译器(JVM中将字节码编译成机器码的部分),JIT编译器可以做一个如果字节码相对简单,即未被优化,效果会更好。
简而言之:不,Java 源代码编译器不会优化这种情况,因为它们并没有真正优化任何东西。他们只做规范要求他们做的事,仅此而已。 javac 和 ecj 开发人员只是为这些情况选择了稍微不同的代码生成策略(大概是出于或多或少的任意原因)。
有关更多详细信息,请参阅 these Stack Overflow questions。
(恰当的例子:现在两个编译器都忽略了 -O
标志。ecj 选项明确这样说:-O: optimize for execution time (ignored)
。javac 甚至不再提及该标志,只是忽略它。)