在Java 8中编译的代码与在Java 11中编译的代码

huangapple go评论70阅读模式
英文:

Compiled code in java 8 vs Compiled code in java 11

问题

我们目前的代码是使用Java 8编译的,但在Java 11虚拟机上运行。
现在我们正试图将我们的代码也迁移到Java 11编译时。想知道在性能方面,使用Java 8编译的代码与使用Java 11编译的代码是否有任何优势,因为两个编译器将生成不同的类文件(字节码)?其中一个在效率方面如何与另一个不同?

英文:

We currently have code compiled in Java 8 but we are running that on Java 11 VM.
Now we are trying to move our code to Java 11 compile time as well. Wondering if there are any benefits to compiled code in Java 8 vs Compiled code in Java 11 performance-wise, since both compilers will produce different class files (bytecode)? How does one differ from the other in terms of efficiency?

答案1

得分: 11

javac 不是优化编译器,因此通常不要期望它在不同版本之间产生“更快”的字节码。优化是 JVM 的任务。

与此同时,Java 编译器确实支持新的语言特性,并且可能支持新的 JVM 特性。其中一些确实会影响性能。JDK 9 到 JDK 11 中最显著的例子如下。

  1. JEP 280:字符串连接优化(JDK 9)。

    这个 JEP 改变了字符串连接表达式的编译方式。在 JDK 9 之前,字符串 + 表达式被翻译成

    new StringBuilder().append()...append().toString();
    

    尽管 JIT 会识别这种链式操作并尝试在运行时进行优化,但这种优化很容易受到破坏,并且并不总是按预期工作。使用 invokedynamic 编译字符串连接可以让 JVM 有更多自由来生成更好的代码。你可以在这个 JEP 的注释中找到详细的解释和基准测试。

  2. JEP 181:基于嵌套的访问控制(JDK 11)

    这个 JEP 解决了访问嵌套类的私有成员的问题。在 JDK 11 之前,Java 编译器为它们生成了合成的桥接方法(示例)。

    乍一看,这与性能无关。然而,在一些极端情况下,额外的合成方法可能会因为内联深度限制而破坏内联。

    基于嵌套的访问控制允许嵌套类在不使用合成桥接的情况下访问彼此的私有成员,从而减少了意外性能下降的风险。

更新

之前我在这个列表中包括了 JDK-8175883:增强 for 循环的字节码生成,但正如 @Holger 在评论中指出的,这个“优化”实际上并没有起作用。

结论

Java 编译器的变化主要与新的语言/JVM 特性有关。字节码级别的优化并不是目标。然而,这些变化中的一些可能会(间接地)影响性能。无论如何,重新编译代码带来的潜在性能提升通常是如此微小,以至于在实际应用中几乎察觉不到。

英文:

javac is not an optimizing compiler, so in general, don't expect it to produce "faster" bytecode from release to release. Optimization is a job of the JVM.

Meanwhile, Java Compiler does support new language features and may support new JVM features. Some of them indeed have performance implications. Most notable examples in JDK 9 - JDK 11 are the following.

  1. JEP 280: Indify String Concatenation (JDK 9).

    This JEP changes the way how string concatenation expressions are compiled. Before JDK 9, string + expression was translated to

    new StringBuilder().append()...append().toString();
    

    Although JIT recognizes such chains and tries to optimize them in runtime, this optimization is fragile and does not always work as expected. Compiling string concatenation with invokedynamic gives the JVM more freedom to produce better code. You may find the detailed explanation and benchmarks in the notes to this JEP.

  2. JEP 181: Nest-Based Access Control (JDK 11)

    This JEP solves the problem of accessing private members of nested classes. Before JDK 11, Java Compiler generated synthetic bridge methods for them (example).

    At first glance, this has nothing to do with performance. However, in marginal cases an additional synthetic method may break inlining due to inlining depth limit.

    Nest-Based Access Control allows nestmate classes to access private members of each other without synthetic bridges, thus reducing risk of accidential performance degradation.

Update

Previously I included JDK-8175883: Bytecode Generation for Enhanced for Loop in this list, but as @Holger noticed in the comments, this "optimization" didn't actually work.

Conclusion

Changes in Java Compiler are mostly related to new language/JVM features. Bytecode level optimization is not a goal. However, some of these changes may (indirectly) affect the performance, too. Anyway, the possible performance benefits from recompiling the code are usually so small that you won't even notice them in a real application.

huangapple
  • 本文由 发表于 2020年10月7日 03:05:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/64232267.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定