Is your software directly installed by your end users? Do you write libraries that could be used on any version of the JVM?
In either of these cases, you don’t have control over which JVM your code is running on, so you need to target your code to the lowest version of the JVM that you can support. In some cases, this could even be Java 6. Even in early 2016, 10% of commercial JVM installations ran on Java 6. Not a lot, but… you know… Enough for you to have to consider it.
This is a problem
I have lived with this situation myself. Besides just keeping us from using the latest language features, supporting older versions allows the possibility that bugs that our users complain about in our software might actually be attributable to underlying JVM bugs. For example, there are known issues with Java.io.File that are fixed with NIO2, older JVM’s generally have lower performance, and finally older JVM’s lack certain security features and other bug fixes. Running on older JVM’s can even expose issues in our code that are not JVM specific but that would be easier to fix on later versions, such as hard-to-find multithreading bugs for parallelism that would be moot if we could just use parallel streams.
Wouldn’t it be nice if we could start using a later version of Java while still supporting older JVM’s for your users? We can!
Multi-Release JAR Files
Allow me to introduce you to JEP-238: Multi-Release JAR Files.
The idea behind Multi-Release JAR Files is that within a single JAR we can include multiple versions of class file for a single class. A version of the class can be selected and loaded that is specific to the version of the JVM running it. Each class file could have a different version of byte code, so later versions of the class will be ignored by older JVM’s. This allows you to begin migration efforts earlier and fix version specific bugs as outlined above, all while still only delivering a single JAR file as usual.
How does this work?
Java 9 defines an extension of the Jar file format. See the diagram below, a picture is worth a thousand words:
jar root - A.class - B.class - C.class - D.class - META-INF - versions - 8 - A.class - B.class - 9 - A.class
As we can see, the normal class files are present right where we’d expect them. But now there are additional class files that represent the same class when run on a specific version of the JVM. So if you run this JAR on Java 6 or 7, you will get the “normal” version of the class. If you run it on Java 8, you will get those versions of class A and B only, and if you run on Java 9, you will get the Java 8 version of B and the Java 9 version of A.
Keep in mind: at the time of this writing, Java 8 is not MRJAR-aware. So the Java 8 portion of the example described in the JEP and shown in this diagram is not actually possible because the Java 8 classes can never be recognized and loaded on Java 8. But they will be loaded on Java 9 and there’s nothing to prevent future versions of the Java 8 JVM from becoming MRJAR aware.
In Part II we will look at an actual implementation that you can do with the early access release of Java 9!