JDK 9 is in the final phases of development, heading towards release on September 21st. The public review consideration ballot for the Java Platform Module System was passed [almost] unanimously, and so everything is now back on track.
You can download the Azul Zulu builds of OpenJDK 9 here.
Recently I’ve been doing a presentation entitled, “55 New Features in JDK 9”. In this, I’ve tried to focus almost entirely on things that are not JPMS. What this has revealed is that there are a few areas of change in JDK 9 that could catch people out regarding compatibility with earlier versions. I’ve also been noticing a number of questions related to these things appearing on Stackoverflow
This blog is a summary of all the things I’ve been able to find that might require you to change something when migrating your applications to JDK 9. What I’m not going to do here is talk about JPMS, encapsulation of non-public classes, effects on reflective access and so on. That’s a whole different set of issues. This collection is just about the other things that you need to be aware of.
Java 9 Updates
- Identifiers. JEP 213, Milling Project Coin brings a small change that hopefully won’t affect many people. In earlier versions of Java, it was possible (though not advisory) to use a single underscore as an identifier (i.e. a variable name). In JDK 9, a single underscore is now a keyword. This may sound bizarre, but is the easiest way of preventing its use as an identifier. The plan is that, possibly in JDK 10, it will be possible to use an underscore as an identifier in Lambda expressions only. This will allow a little cleaner syntax for the situation where your Lambda has a single parameter, which is not used in the body of the Lambda (e.g.
_ -> foo()
) - The layout of the JDK has changed (JEP 220): If you rely on the location of JDK specific files relative to
$JAVA_HOME
, you may need to make some changes. There’s a number of distinct differences here:
- There is no more separate jre sub-directory, with a separate copy of the bin, lib, and so on directories. The JDK directory structure is now flat with a single bin directory (and therefore only one copy of the java executable), the lib directory is for native libraries and the jmods directory for the JDK modules. There is also a new conf directory that contains all files that, as administrator, you might need to change to modify the JDK configuration. This has things like networking and logging properties.
- The switch to modules means that there are no more
rt.jar
ortools.jar
files. If you access these directly, you will need to change your code. - The hprof agent has been removed from JDK 9 (JEP 240) and as a consequence the
lib/libhprof.so
(orbin/hprof.dll
on Windows) files have been deleted as well.
- The JDK version string (JEP 223): The old way of defining the version of the JDK was rather confusing. We had
JDK 8u131
, for example, but if you runjava -version
you’ll get backjava version "1.8.0_131"
. To simplify this for both humans and applications, the format is nowJDK $MAJOR.$MINOR.$SECURITY.$PATCH
, sojava -version
on JDK9 returnsjava version "9"
(this will change once the release is final and we start getting updates). The important thing here is that, if you use the version string of the JVM in your code and rely on the current format you’ll have to make changes to run correctly on JDK 9. Thread.stop(Throwable)
now throws anUnsupportedOperationException
, which it did not do before. This method has been deprecated since deprecation first started due to its inherent lack of thread safety. The other version ofThread.stop()
, which takes no arguments will still work without throwing an exception (but is still deprecated and you are strongly advised not to use this).- The Java Network Launch Protocol (JNLP) has been updated to allow strict parsing of the configuration files. The format used now conforms to the XML specification by requiring “&” version-range combiner be expressed as “&”. Parsing of the configuration is now strict, meaning some files that would have worked with older versions of Java will now produce errors. More details can be found on the JSR 52 maintenance page.
- Related to JPMS the extension mechanism (for optional packages) and the endorsed standards override mechanism have both been removed and replaced with JPMS equivalents. As a consequence, the
$JAVA_HOME/lib/ext
and$JAVA_HOME/lib/endorsed
directories have been deleted. If you recreate these and try putting things in them in the hope that this will still work, it won’t. The JVM will fail to start of it finds these directories, and you will get the following error message:<JAVA_HOME>/lib/ext exists, extensions mechanism no longer supported; Use -classpath instead. Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.
- Six previously deprecated public methods have been deleted. This is a pretty big deal, as these are the first APIs to be eliminated in the history of Java. The methods that have been removed are the
addPropertyChangeListener()
andremovePropertyChangeListener()
from thejava.util.jar.Pack200
,java.util.jar.Unpack200
andjava.util.logging.LogManager
classes. - The
com.sun.security.auth.callback.DialogCallbackHandler
class, which was part of the Java Authentication and Authorization Service (JAAS), has been removed. This was deprecated in JDK 8. - JRE version selection is no longer available. There were two ways of doing this in the past. The first was from the command line using the
-version:<version-id>
option. If you use this, the JVM will abort without starting. The second way was from the manifest of a jar file. In this case, JDK 9 will ignore the directive but start up normally. It was felt that forcing people to change the manifest was too onerous compared to changing the command line. Please note that the-version
option (without a following colon and version-id) still works quite happily to report which version of the Java runtime you have. - Deprecated GC options have been removed (JEP 214). There were a number of specific GC options and combinations of options that were deprecated in JDK 8 (JEP 173). These will not now be recognised and will cause the JVM to abort when it starts. The options to be aware of are shown below
-XX:-UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -Xincgc -XX:+CMSIncrementalMode -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+UseConcMarkSweepGC -XX:-UseParNewGC -XX:+UseCMSCompactAtFullCollection -XX:+CMSFullGCsBeforeCompaction -XX:+UseCMSCollectionPassing
In JDK 9, the incremental mode of concurrent-mark-sweep (iCMS) has been removed, and the current plan is to remove CMS completely in JDK 10
Command line options
This seems to be quite a big change to me, yet I’ve not seen it highlighted (or well documented) so far.
Aside from the GC options noted above, there is a significant change in how logging is handled by the JVM. JEP 158 introduces the Unified Logging system, and this is used by GC logging to provide Unified GC logging (JEP 271). This affects a number of commonly used GC logging command line options as well as some other things.
To figure out exactly what had changed I extracted the relevant lines from the OpenJDK source code (specifically, the src/share/vm/runtime/globals.hpp
file). By doing a diff, I was able to get a list of all the -XX
options that had been removed from JDK 8 and all those that had been added to JDK 9. I then wrote a script to use each in turn with a simple app to see what the result of using the options that had been removed from JDK 8 was on JDK 9.
The changes to -XX
options can be separated into three groups:
Ignored Command Line Options
If you use one of these, you will get the following warning message, but the JVM will start normally.
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option <Option>; support was removed in 9.0
AdaptiveSizePausePolicy
CodeCacheMinimumFreeSpace
DefaultThreadPriority
JNIDetachReleasesMonitors
LazyBootClassLoader
NmethodSweepCheckInterval
NmethodSweepFraction
PrintOopAddress
ReflectionWrapResolutionErrors
StarvationMonitorInterval
ThreadSafetyMargin
UseAltSigs
UseBoundThreads
UseCompilerSafepoints
UseFastAccessorMethods
UseFastEmptyMethods
BackEdgeThreshold
PreInflateSpin
<h3id=”DeprecatedOptions”>Deprecated Command-Line Options
These options will issue a warning when the JVM starts, either telling you which option will be used instead by the JVM or telling you which option you should use. Most of these are related to the move to unified GC logging.
Here are two examples of the warning messages:
warning][gc] -XX:+PrintGC is deprecated. Will use -Xlog:gc instead. Java HotSpot(TM) 64-Bit Server VM warning: Option CreateMinidumpOnCrash was deprecated in version 9.0 and will likely be removed in a future release. Use option CreateCoredumpOnCrash instead.
The table below shows the JDK 8 command line option and the JDK 9 replacement.
JDK 8 Option | JDK 9 Replacement |
PrintGC |
-Xlog:gc |
PrintGCDetails |
-Xlog:gc* |
CreateMinidumpOnCrash |
CreateCoredumpOnCrash (suggestion) |
DefaultMaxRAMFraction |
MaxRAMFraction (suggestion) |
TraceBiasedLocking |
-Xlog:biasedlocking=info |
TraceClassLoadingPreorder |
-Xlog:class+preorder=debug |
TraceClassResolution |
-Xlog:class+resolve=debug |
TraceMonitorInflation |
-Xlog:monitorinflation=debug |
TraceRedfineClasses |
-Xlog:redefine+class*=info |
TraceSafepointCleanupTime |
-Xlog:safepoint+cleanup=info |
TraceClassLoading |
-Xlog:class+load=info |
TraceLoaderConstraints |
-Xlog:class+loader+constraints=info |
ConvertSleepToYield |
NONE |
Options That Won’t Work
These are the most significant options regarding compatibility. If you have used these in the past and don’t change your command line, your application won’t start. When you use one of these you will get the following error message:
Unrecognized VM option '<Option>' Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.
Here’s the list of 50 options that won’t work with JDK 9. Check to see if you’ve used any of these in your command line and startup scripts.
AdjustConcurrency
CMSCompactWhenClearAllSoftRefs
CMSDumpAtPromotionFailure
CMSFullGCsBeforeCompaction
CMSIncrementalDutyCycle
CMSIncrementalDutyCycleMin
CMSIncrementalMode
CMSIncrementalOffset
CMSIncrementalPacing
CMSParPromoteBlocksToClaim
CMSPrintEdenSurvivorChunks
CollectGen0First
GCLogFileSize
NumberOfGCLogFiles
ParallelGCVerbose
PrintAdaptiveSizePolicy
PrintCMSInitiationStatistics
PrintCMSStatistics
PrintClassHistogramAfterFullGC
PrintClassHistogramBeforeFullGC
PrintFLSCensus
PrintFLSStatistics
PrintGCApplicationConcurrentTime
PrintGCApplicationStoppedTime
PrintGCCause
PrintGCDateStamps
PrintGCTaskTimeStamps
PrintGCTimeStamps
PrintHeapAtGC
PrintHeapAtGCExtended
PrintJNIGCStalls
PrintOldPLAB
PrintPLAB
PrintParallelOldGCPhaseTimes
PrintPromotionFailure
PrintReferenceGC
PrintTLAB
PrintTenuringDistribution
TraceDynamicGCThreads
TraceGen0Time
TraceGen1Time
TraceMetadataHumongousAllocation
TraceParallelOldGCTasks
UseCMSCollectionPassing
UseCMSCompactAtFullCollection
UseGCLogFileRotation
UseMemSetInBOT
UsePPCLWSYNC
UseVMInterruptibleIO
WorkAroundNPTLTimedWaitHang
Hopefully, this will help you check whether there are any changes you need to make to your application and its configuration as you migrate to JDK 9.