3

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?

Sagar
  • 5,315
  • 6
  • 37
  • 66
  • 2
    You can decompile both of them with `javap` and compare. I bet there's not much difference, if any at all. Performance comes from JIT compiler, not the bytecode. – Pavel Smirnov Oct 06 '20 at 19:10
  • 1
    @PAvelSmirnov, not quite, there is a sizable difference, it was added in Java 9: https://openjdk.java.net/jeps/280 Update: Also, there's this: https://openjdk.java.net/jeps/181 – M. Prokhorov Oct 06 '20 at 19:43

1 Answers1

11

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.

apangin
  • 92,924
  • 10
  • 193
  • 247
  • 2
    JDK-8175883 seems to be a phantom fix. I tried `for(String s: List.of("foo", "bar")) {System.out.println(s);} foo();` and there is no difference in the bytecode between JDK 9 and JDK 10 (nor any JDK up to 16). The `Iterator` reference is dangling during the `foo()` invocation as it ever was. Not that it matters to me, as I don’t consider inserting explicit nulling statements an improvement. It would make the bytecode of every for-each loop bigger, to serve a rare corner case that is otherwise never handled, e.g. try-with-resource, finally, etc. may still introduce dangling synthetic variables. – Holger Oct 07 '20 at 07:44
  • 1
    As an addition, [as discussed in this answer](https://stackoverflow.com/a/25746587/2711488), the compiled form of try-with-resource statements has improved significantly with JDK 11. – Holger Oct 07 '20 at 10:14
  • 1
    @Holger Good catch. It turns out that for-loop "optimization" was (temporarily?) backed out in [JDK-8194836](https://bugs.openjdk.java.net/browse/JDK-8194836) and never returned. The mention of this optimization in [JDK 10 Release Notes](https://www.oracle.com/java/technologies/javase/10-relnote-issues.html) confused me. Thanks for double checking. – apangin Oct 07 '20 at 16:56
  • As to try-with-resource change, do have evidence of it affecting performance? – apangin Oct 07 '20 at 16:59
  • 1
    I don’t think that it affects runtime performance, as most artifacts were dead code. Only a few conditionals that only matter while still running interpreted. But the verifier surely will have less work. And there’s less code to load. I guess, you’d need *lots of* try-with-resource statements (nested, to max it out), to notice a difference. – Holger Oct 07 '20 at 17:03