How to Upgrade from Java 8 to Java 12: Benefits and Troubleshooting Tips

How to Upgrade from Java 8 to Java 12: Benefits and Troubleshooting Tips

Author: Trisha Gee

Translator: Zhang Weibin

Key Points

  • Since Java 8, many useful new language features, tools, and performance improvements (especially related to garbage collection optimizations) have been introduced.

  • When choosing to upgrade, we face the decision of whether to upgrade to the latest Java (12) and prepare for upgrades every six months, or to upgrade to the latest LTS (11) version, which gives us three years to consider the next upgrade.

  • Do not ignore compiler warnings. In modern Java, deprecated features are taken more seriously, and APIs were removed in Java 10 and Java 11.

  • One change introduced in Java 9 is that internal APIs (mostly classes in packages starting with sun.misc.*) have been hidden, making them unusable. In Java 11, non-core JDK APIs were also removed. These changes may affect your applications, but there are clear migration paths to avoid these issues.

  • After getting through the initial upgrade challenges, we can at least test our applications on the latest version of Java every six months, such as in a CI environment.

Generally speaking, enterprise applications are reluctant to upgrade to the latest version of Java unless it has been thoroughly proven. Since Java 9 was released in September 2017, a new version of Java has been released every six months, making everything more challenging. So, despite Java 9 being less than two years old, the latest version is already Java 12.

This pace of change can be daunting, and upgrading from Java 8, which is widely used in mainstream applications, to Java 12 may involve a lot of work. In this article, we will look at:

  • The benefits of upgrading;

  • Potential issues that may arise during the upgrade process;

  • Some tips regarding the upgrade.

Why Upgrade?

Before delving into the details of how to upgrade, we should seriously consider why we want to upgrade.

Features

As developers, we are most concerned with the new features and APIs that each language upgrade brings. Since Java 8, we have seen many new features and new tools that change the way we work. Here are some new features that developers believe make their work easier in the latest version of Java.

Local-variable type inference (i.e., var) is a great example of syntactic sugar that helps reduce boilerplate code. It is not a solution to all readability issues, but using it in the right places can help code readers focus on business logic (what it does) rather than boilerplate code (how it does it).

Convenience Factory Methods for Collections significantly simplify the creation of collections, such as List, Map, and Set. These factory methods can also create unmodifiable collections, making them safer to use.

Collecting to Immutable Collections is a feature that allows using a new Collector for Streams operations that will place results into an immutable collection.

Predicate::not provides a convenient way to negate predicate lambda expressions or method references, which also reduces our boilerplate code.

New methods on Optional give us more functional programming options when using Optional to replace cumbersome if statements.

JShell is a REPL that allows us to run lines of Java code, even scripts. It is a great way to experience new features, and we can use it in our local development environment to avoid adopting the new version of Java in production application code.

HttpClient is built into the JDK. Almost everyone uses HTTP in some form, either through web applications or REST services. The built-in client removes external dependencies while supporting both synchronous and asynchronous programming models for HTTP 1.1 and 2.

Multi-release JAR files are a tool library that developers can use to support scenarios requiring the latest version of Java while also supporting older versions.

JLink is a fantastic tool provided by the Java Module System that allows us to package and deploy only the parts of the JDK that we actually need.

Performance

Generally speaking, new versions tend to perform better than previous ones. “Better” can manifest in various forms, but in recent releases, we have seen improvements in startup time, reduced memory consumption, and code that uses fewer CPU cycles by utilizing specific CPU instructions. Java 9, 10, 11, and 12 have all made significant changes and improvements to garbage collection, including changing the default garbage collector to G1, improvements to G1, and three experimental garbage collectors (Epsilon and ZGC in Java 11, and Shenandoah in Java 12). While three collectors may seem excessive, each collector is optimized for different use cases, allowing you to choose the modern garbage collector that best fits your application.

Cost Reduction

Improvements in recent versions of Java may lead to cost reductions for you. Tools like JLink can reduce the size of deployment artifacts, and improvements in memory usage can lower cloud computing costs.

Another potential benefit is that using modern versions of the language can attract more developers, as many developers are eager to learn new things and update their skills. Using modern versions of Java is beneficial for developer retention and recruitment, ultimately impacting team operating costs.

Considerations for Upgrading

Since Java 8, many changes have occurred, not limited to language-level features. Oracle has started releasing two different build versions with different licenses (one being a commercial build that requires payment for production use, and the other being the open-source OpenJDK build), and they have changed their upgrade and support model. This has sparked much debate in the community, but ultimately, we will have more choices regarding which JDK to use. We must consider what approach to adopt and what we want from the JDK.

Which Version to Choose?

Given that the latest version is Java 12, it seems we have a lot of options when upgrading from Java 8. In reality, this choice is much simpler. Now, we have a release every six months, and each new release replaces the previous one. The only exception is that every three years, there will be a Long Term Support (LTS) release. This allows organizations to choose the most suitable upgrade path, either upgrading to the latest version every six months or adopting the traditional method of a major upgrade every three years to the LTS version. Java 8 is an LTS version, but Oracle stopped providing (free) updates for it in commercial applications as of January this year. Java 11 is the current LTS version, although Oracle has not yet provided free commercial updates for it since releasing Java 12. However, many organizations provide a variety of JDK build versions that will offer at least three years of updates.

Your choice is to either upgrade to the latest version of Java (12) and prepare for updates every six months, or upgrade to the latest LTS version (11), giving you three years to consider the next upgrade. We recommend that larger organizations adopt the strategy of upgrading from LTS version to the next LTS version, while smaller startup organizations can upgrade every six months. The benefits of a faster and more predictable release cadence support both approaches.

Which Build Version to Use?

We cannot simply assume that Oracle’s lack of free updates for LTS versions means we cannot use LTS versions in production and receive free updates. Many organizations and vendors now provide builds of OpenJDK (the reference implementation of JDK, on which Oracle’s commercial JDK is even based). I will not list them all here; please refer to this list, which covers the providers of free OpenJDK builds, how long free public updates can be maintained, and details of commercial support.

It seems that this is much more complicated than before. Of course, there are many other factors to consider, which are side effects of having more choices. Java Champions are preparing a more comprehensive discussion on licenses, support, updates, and different options, with a Q&A session on this topic at QCon London 2019.

Will Java 9 Break Everything?

Many developers’ main concern when upgrading from Java 8 is the significant changes introduced in Java 9, fearing that these changes will break their applications. One change is the encapsulation of internal APIs, meaning that some previously available methods in the JDK are no longer usable. When Java 9 was released, this, along with the removal of tools.jar and rt.jar, caused many to worry, but it turned out that these issues were more severe for library, framework, and language developers than for application developers.

If your application does not do anything it shouldn’t (like using internal APIs or deprecated methods), migrating to Java 9 or higher is not as scary as it seems. Many of the issues faced by the community have actually been resolved by the build tools and libraries we use.

Upgrade Guide

Every upgrade process is related to the application being migrated. However, there are some basic best practices that can help simplify the process. We have listed them here in the order they should be addressed, and you will find that some of the initial steps do not require upgrading the JDK version at all.

Handling Compiler Warnings

Warnings appear for a reason; if you encounter warnings, they usually indicate features that will disappear in the future. If you are using Java 8 on the Java 8 JDK, you may see deprecation warnings or warnings about features that should not be used (see Figure 1). Before attempting to upgrade to a newer version of the JDK, please resolve these warnings.

How to Upgrade from Java 8 to Java 12: Benefits and Troubleshooting Tips

Figure 1: Example of Compiler Warnings from JDK 8

Note that in modern Java, deprecated features are taken more seriously, and APIs were removed in Java 10 and Java 11.

Check for Internal API Usage

One change in Java 9 is that internal APIs (mostly classes starting with sun.misc.*) have been hidden and can no longer be used.

There is a tool in the JDK called jdeps that you can run with the -jdkinternals flag to check if a set of classes is using something it shouldn’t. For example, if I run the following command in my project’s output:

$JAVA_HOME\bin\jdeps -jdkinternals .

I will get the following output:

. -> /Library/Java/JavaVirtualMachines/jdk1.8.0_201.jdk/Contents/Home/jre/lib/rt.jar   com.mechanitis.demo.sense.twitter.connector.TwitterOAuth (.)    -> sun.misc.BASE64Encoder    JDK internal API (rt.jar)    -> sun.misc.Unsafe           JDK internal API (rt.jar)
Warning: JDK internal APIs are unsupported and private to JDK implementation that are subject to be removed or changed incompatibly and could break your application. Please modify your code to eliminate dependency on any JDK internal APIs. For the most recent update on JDK internal API replacements, please check: https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
JDK Internal API                Suggested Replacement----------------                ---------------------sun.misc.BASE64Encoder          Use java.util.Base64 @since 1.8sun.misc.Unsafe                 See http://openjdk.java.net/jeps/260

This tool not only identifies classes that use internal APIs but also provides suggestions for what to use as replacements.

Upgrade Build Tools

If you are using Maven or Gradle, you will also need to upgrade.

Gradle

Gradle 5.0 introduced support for Java 11, so we need to use at least version 5.0. The current latest version is 5.4.1, which provides support for Java 12.

Maven

We need to use at least version 3.5.0 (the latest version is 3.6.1), and ensure that the Maven Compiler Plugin is at least version 3.8:

<plugin>   <groupId>org.apache.maven.plugins</groupId>   <artifactId>maven-compiler-plugin</artifactId>   <version>3.8.0</version>   <configuration>       <release>11</release> <!-- or 12 -->   </configuration></plugin>

Update Dependencies

Many of the issues you hear about migrating to Java 9 or higher are related to libraries and frameworks (and they may have already been fixed). For example, many frameworks use reflection and internal APIs behind the scenes. To ensure your application continues to run smoothly, you need to ensure that all your dependencies are up to date. Many libraries have been updated to support Java 9 and higher, and the community is working hard to ensure this process continues.

Some dependencies may need to be replaced. For example, many libraries have used Byte Buddy for code generation and proxies because it is compatible with all modern versions of Java. When considering migration to Java 11, you must understand your dependencies and determine whether they have been updated to support versions of Java after 8.

Add Dependencies for Removed Features

Non-core JDK APIs have been removed, including Java EE and Corba modules as well as JavaFX. This issue is usually straightforward to resolve; you just need to add the correct libraries in your dependency management. For example, add a dependency for JAXB in Gradle or Maven. JavaFX is a bit more complex, but there is excellent documentation on the OpenJFX site.

Run Applications with the New JDK

To use the latest version of Java, you do not need to recompile, which is one of the reasons language developers work hard to maintain backward compatibility. You can run your application in a continuous integration environment (for example) using the new JDK without any code changes.

Compile with the New JDK

In the previous steps, we can still use Java 8 to compile the application. Only after completing these steps should you consider compiling for Java 11 or 12. Remember, if you do not want to use new features, you can compile the application with a lower language version, allowing you to roll back to the old version. For example, in Maven:

<plugin>   <groupId>org.apache.maven.plugins</groupId>   <artifactId>maven-compiler-plugin</artifactId>   <version>3.8.0</version>   <configuration>        <release>8</release>   </configuration></plugin>

In Gradle:

sourceCompatibility = JavaVersion.VERSION_1_8

Start Using New Features

If everything runs smoothly, all tests pass, and all features perform well, even after running safely in production for a while, we can consider using the new language features.

Additionally, keep in mind that although Java 9 was entirely about the Java Module System, applications do not necessarily have to use it, even when we have migrated to a version that supports that feature. However, if you are interested in adopting the module system, we have a guide on using it on InfoQ.

Conclusion

Many changes have occurred since Java 8: a release every six months; changes in licenses, updates, and support; and possibly changes in the source of the JDK. In addition, there are, of course, new language features, including significant changes introduced in Java 9. However, now that Java 11 has replaced Java 8 as the latest LTS version, and major libraries, frameworks, and build tools have adopted the latest version of Java, it is a good time to upgrade applications to Java 11 or 12.

After getting through the initial upgrade challenges, we can at least test our applications on the latest version of Java every six months, such as in a CI environment. Ideally, you can even follow the six-month release cadence, allowing you to use the latest version of Java and take advantage of new features as soon as they are available.

About the Author

Trisha Gee is a Java Champion who has developed Java applications for companies of various sizes across multiple industries, including finance, manufacturing, software, and non-profit organizations. She has extensive experience in high-performance Java systems and is passionate about improving developer productivity and contributing to open-source projects. She is a Java Developer Advocate at JetBrains, which allows her to stay at the forefront of Java technology.

Original link:

https://www.infoq.com/articles/upgrading-java-8-to-12/

How to Upgrade from Java 8 to Java 12: Benefits and Troubleshooting Tips

Click to view and reduce bugs 👇

Leave a Comment