Many users have already upgraded from Java 7 to Java 8, to benefit from improvements in speed, brevity of code, and lower memory usage. Other users have asked for more prescriptive guidance of the upgrade: when to make the change, what to expect, and how to do it in a controlled manner.
A previous post, Upgrading Major Java Versions, provides details for certain stakeholders: support timelines, compatibility guides, lists of changes, and different supporting material.
This post is intended to provide more prescriptive guidance of upgrading your Java SE version: how to test components and features designed to control behavior and upgrade part of your environment in stages.
The decision to upgrade is not only for companies that develop software; it also applies to users running software built by others. In many cases, users can see significant speed improvements without recompiling, simply by upgrading the runtime.
The previous post explains when to upgrade in relation to platform support. When upgrading infrastructure, it is important to segment the architecture. Rather than upgrading everything at the same time, separate it into different environments so that you can test each one on its own.
Typical environments, in my preferred order of when to upgrade:
High level upgrade plan: Upgrade the build and test environments but keep things targeted for production. Once you are ready, upgrade the production environment and begin taking advantage of new features.
The JDK is backwards compatible, meaning that JDK 8 can run binaries produced by JDK 7 and JDK 6. The javac compiler can also produce binaries targeting earlier versions of the JDK. In planning to upgrade, we will use this capability.
The important similarity between developer workstations and central build servers is that they are used to compile the application from source code into binary artifacts, such as JAR and WAR.
Upgrading a workstation or build server involves upgrading its JDK installation. The same system may run multiple versions of the JDK side-by-side, making it your choice if you want to uninstall the older one.
If you choose to run multiple versions, just be mindful of two environment variables:
Test your upgrade the following commands:
Personal Tip: On my Windows 7 laptop, I regularly switch Java version to test things. To counter forgetfulness, I set my JAVA_HOME variable first, and then my PATH uses that JAVA_HOME. By doing this, I only need to change one thing. My PATH starts with: %JAVA_HOME%\bin;
By using your upgraded installation to cross-compile, you will produce artifacts that run in your not-yet-upgraded test and production environments.
The javac compiler provides three options to control compatibility: -bootclasspath -source and -target. Without the -source flag, the compiler won’t warn you about using language features that may not be available on your earlier target JDK platform. Without the -target flag, the compiler won’t produce binaries that can run on your earlier target JDK. Finally, without the -bootclasspath flag, the compiler won’t be able to find the correct version of the core class libraries from the earlier JDK. A simple example of using all flags correctly can be found in the javac documentation’s Examples section.
Configure the build to specify the -source and -target of your runtime.
Building your application with the latest JDK will identify any potentially problematic areas. Investigate compiler errors, if any.
Although compiler warnings do not cause build failures, they indicate areas of interest. Looking into them provides a safeguard of something that may affect compatibility or legibility of code.
In JDK 8, we added special indicators to point where incompatibilities may occur in the future.
com/example/App.java:[32,24] SOMETHING is internal proprietary API and may be removed in a future release
In this example, my code has made use of a JDK internal API on line 32 of App.java. If you see this message, your code will likely still work for now but you should consider moving towards the known-compatible replacement for that API.
OpenJDK 8 features a new dependency analysis tool, jdeps, designed to identify where applications or their dependencies make use of internal JDK APIs. Usage of these APIs does not currently indicate incompatibility, rather they point out where you use non-public and unsupported internal APIs.
If jdeps identifies usage in your code, consider switching to the public replacement APIs. If jdeps identifies usage in third party code, you may still be impacted in the future. Consider upgrading those identified dependencies, patching them, or identifying an alternative.
We have previously limited access to some internal APIs in some situations. The publicly supported APIs are still available, unaffected, and fine to use.
Consider additional tools for analysis as well:
Upgrading the test and production environments will allow you to evaluate an application and see how it interacts with other systems.
The JDK is designed to be backwards compatible. Upon upgrading the test environment, it is your choice if you want to run the same cross-compiled binary.
When testing your application, it is important not to change too many things at once. For example if you test the upgrade while simultaneously testing a complete rewrite of major components at the same time, it will be hard to tell which issue came from which cause. Given time available for testing, is may not be feasible to test the upgrade alone, without any other changes. Isolate changes as best you can and do not pre-assign a root cause to any issue before investigation.
Once you have successfully upgraded your environment(s), there is no more reason to cross-compile your binaries. Consider going back to your build environment and removing the -bootclasspath, -source, and -target flags.
The previous post, Upgrading Major Java Versions, provides
links to detailed information and compatibility guides about what changed
between different versions.
In the interest of brevity, I will list a few noteworthy differences that I have seen:
Many applications are able to leverage a smooth upgrade path from JDK 7 to JDK 8, in order to benefit from improvements like speed and more concise code. Examples of teams that have successfully migrated include: