java 8 中的编译代码与 java 11 中的编译代码

Sag*_*gar 2 java jvm compilation java-8 java-11

我们目前有在 Java 8 中编译的代码,但我们正在 Java 11 VM 上运行它。现在我们也在尝试将我们的代码移动到 Java 11 编译时。想知道 Java 8 中的编译代码与 Java 11 中的编译代码在性能方面是否有任何好处,因为两个编译器都会生成不同的类文件(字节码)?一个在效率方面与另一个有何不同?

apa*_*gin 9

javac不是一个优化的编译器,所以在一般情况下,不要指望它产生从发布的“更快”的字节码释放。优化是 JVM 的工作。

同时,Java Compiler确实支持新的语言特性,并且可能支持新的 JVM 特性。其中一些确实具有性能影响。JDK 9 - JDK 11 中最著名的例子如下。

  1. JEP 280:指示字符串连接(JDK 9)。

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

    new StringBuilder().append()...append().toString();
    
    Run Code Online (Sandbox Code Playgroud)

    尽管 JIT 识别出这样的链并尝试在运行时优化它们,但这种优化是脆弱的,并不总是按预期工作。编译字符串连接invokedynamic使 JVM 可以更自由地生成更好的代码。您可以在此 JEP的注释中找到详细的解释和基准。

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

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

    乍一看,这与性能无关。但是,在边缘情况下,由于内联深度限制,额外的合成方法可能会破坏内联。

    基于嵌套的访问控制允许嵌套类在没有合成桥的情况下访问彼此的私有成员,从而降低意外性能下降的风险。

更新

以前我在这个列表中包含了JDK-8175883:增强循环的字节码生成,但正如@Holger 在评论中注意到的那样,这种“优化”实际上并没有奏效。

结论

Java Compiler 中的更改主要与新语言/JVM 功能有关。字节码级优化不是目标。但是,其中一些更改也可能(间接)影响性能。无论如何,重新编译代码可能带来的性能优势通常很小,您甚至在实际应用程序中都不会注意到它们。

  • JDK-8175883 似乎是一个幻影修复。我尝试了 `for(String s: List.of("foo", "bar")) {System.out.println(s);} foo();` 并且 JDK 9 和 JDK 之间的字节码没有区别10(也不是任何 16 以下的 JDK)。在调用“foo()”期间,“Iterator”引用一如既往地悬空。这对我来说并不重要,因为我不认为插入显式空语句是一种改进。它会使每个 for-each 循环的字节码更大,以服务于罕见的极端情况,否则永远不会处理,例如 try-with-resource、finally 等,但仍可能引入悬空合成变量。 (2认同)