为什么Java编译器不会重写这段代码?

5

我正在使用这段代码进行测试:

public class TestNull {
   public void leftComparison(String s) {
       if (s == null);
   }
   public void rightComparison(String s) {
       if (null == s);
   }
}

我使用javac 1.8.0_05编译了它,然后检查了字节码:

public class TestNull {
  ....
  public void leftComparison(java.lang.String);
    Code:
       0: aload_1       
       1: ifnonnull     4
       4: return        

  public void rightComparison(java.lang.String);
     Code:
       0: aconst_null   
       1: aload_1       
       2: if_acmpne     5
       5: return        
}

显然,leftComparison 编译后在栈上推送和弹出1个变量,而 rightComparison 推送和弹出2个变量。我猜测,leftComparisonrightComparison 稍微更有效率一些?

我想知道为什么编译器不重写 rightComparison 的代码呢?我认为这两个比较应该在语义上是等价的,不是吗?


3
如果编译器的任务是将你的代码重写成更简单但等效的代码,那么它不会生成任何字节码,因为这些方法什么也不做。 :-) - Wyzard
因为没有ifnots字节码,javac在翻译时非常逐字逐句。 - Hot Licks
3个回答

5
Java的字节码编译器几乎不进行优化。几乎所有的严重优化工作都是由JIT编译器完成的。
“我想知道为什么编译器不重新编写rightComparison的代码?”
因为没有必要重写它。JIT编译器应该能够处理这两个版本,并且很可能为两个版本生成最佳(本地)代码。(如果您感兴趣,可以检查此内容。有方法可以查看JIT编译器生成的本机代码。)
(另请参见@codenheim的答案以获得更多技术解释。)
“在我看来,这两个比较应该在语义上等效,对吗?”
是的...但这并不意味着字节码编译器有义务为两个版本生成相同的字节码序列。
真正的教训在于,由字节码编译器生成的字节码序列告诉您有关代码实际执行方式的信息非常少。从阅读字节码中得出的任何性能结论都是高度可疑的。

好的解释。谢谢。 - James

2
在实践中,字节码相当于中间代码。过早优化或重复优化的投资回报率较低。
它会引入不必要的维护开销。
最好在了解目标平台后进行优化。Java堆栈指令集本身并不适合高级优化。它的设计过于简单。通过重新排列一些基于堆栈的操作码,我们只能完成有限的“优化”工作。虽然在javac中可以进行许多高级“显而易见”的优化,但我认为实用主义在这里取得了胜利,并将其推迟到JIT,后者可以利用更丰富的本地指令集和技术(因平台而异),这些技术在字节码中无法使用。

1
基本上是因为“代码是这么写的”。比较操作会考虑到左侧变量并将其与右侧进行比较。当你说 null == s时,你要做的是“将null与该变量进行比较”,所以代码定义了一个常量null来进行比较。编译器只需将另一个比较优化为非空检查即可。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接