JDK 9 中一些需要提防的坑
JDK 9正处于开发的最后阶段, 向着9月21号的发布目标冲刺。Java 平台模块系统的公开评审投票基本上被一致通过,所以目前一切都可以回归正常了。
你可在此处下载 OpenJDK 9:http://zulu.org/zulu-9-pre-release-downloads/ (尝鲜版)。
最近我在写一篇文章,题目是“JDK 9 中的 55 个新特性”。在其中,我试图把重点放在所有非 JPMS 内容上。这也表明 JDK 9 中有几个改动的部分,可以让人们了解到其与早期版本的兼容性。我注意到也有一些与之相关的问题出现在了 Stackoverflow 上。
这篇博客是我能够找到的你在将应用程序迁移到 JDK 9 时所有可能需要进行改动的内容的总结。我不会在此处讨论 JPMS 、非公共的封装类、对反射访问的影响等。本文是一系列不同问题的集合。这个集合只涉及一些你需要注意的其他事情。
1. 标识符. JEP 213, Milling Project Coin 带来了一些变化,希望这些不会影响到太多的人。
早些版本的Java,可以使用一个下划线来作为标识符,虽然不被建议这样使用,但是确实可以使用单个下划线作为一个变量名。在 JDK9 中,单个下划线变成了一个关键字。这可能听起来和奇怪,但是最简单的方式就是不使用单个下划线作为标识符。可能将来在 JDK10 中,单个下划线只能用于 lambda 表达式的标识符。这样将使 lambda 表达式可以使用单个参数,从而使主体语法更清晰(例如: _ -> foo())
2. JDK 的布局发生了变化 (JEP 220): 如果你依赖于 $ JAVA_HOME 中特定的文件,你可能需要做一些更改。
具体需要的更改如下:
不再有单独的 jre,bin,lib 等子目录。 JDK 目录结构现在与单个 bin 目录平行(因此只有一个 Java 可执行文件的副本),lib 目录用于本机库和 JDK 模块的 jmods 目录。还有一个新的 conf 目录,可以让管理员更改 JDK 配置。Conf 中有网络和日志属性。
这个转变意味着不再有 rt.jar 或 tools.jar 文件。你需要更改代码来访问这些文件。
hprof 代理已从JDK 9(JEP 240)中删除,因此 lib / libhprof.so(或 Windows 上的 bin / hprof.dll )文件也被删除。
3. JDK 版本字符串(JEP 223):老的定义 JDK 版本的方法相当混乱。例如,我们有 JDK 8u131 ,但是如果你运行 java -version ,你会得到 java version "1.8.0_131" 。
为了从人类和应用程序角度简化这个(定义版本的方法),现在的版本格式是 JDK $MAJOR.$MINOR.$SECURITY.$PATCH ,所以在 JDK9 上的 java -version 将返回 java version "9"(一旦发布最终的 release 版,我们获取更新之后该版本号就会改动)。这里重要的是,如果你在代码中使用 JVM 的版本字符串,并依赖于当前版本格式,那么在 JDK 9 中你必须进行代码改动才能正确运行。
4. Thread.stop(Throwable) 现在会抛出一个 UnsupportedOperationException ,它之前并没有抛出类似异常。
由于其自身缺乏线程安全性,因此不推荐使用此方法。另一个不带参数的 Thread.stop() 版本仍然可以使用,并且不会抛出异常(但仍然不推荐使用,并强烈建议不要使用此函数)
5. Java 网络登录协议(Java Network Launch Protocol ,JNLP)已更新,以支持严格的配置文件解析。现在使用的格式符合 XML 规范,要求“&”版本范围连接器表示为“&”。
配置解析目前是严格的,这意味着一些在旧版本的 Java 中可以使用的文件现在将会产生错误。 有关更多详细信息,请参见JSR 52维护页面。
6. 与 JPMS 相关的扩展机制(可选软件包)和已批准的标准覆盖机制都已被删除,并使用 JPMS 对应物替代。
因此,$JAVA_HOME/lib/ext 和 $JAVA_HOME/lib/endorsed 的目录已被删除。如果你重新创建这些目录,并尝试把东西放在这些目录下,祈祷他们能工作,这是不可能的。JVM 在查找到这些目录时将无法启动,你将收到以下错误消息:
/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.
7. 六个之前已弃用的公共方法已经被删除。这是一件相当大的事情,因为这些是 Java 历史上第一次要删除的API。
已删除的方法包括java.util.jar.Pack200,java.util.jar.Unpack200中的addPropertyChangeListener()、removePropertyChangeListener(),以及 java.util.logging.LogManager类。
8.作为Java Authentication and Authorization Service (JAAS,Java授权认证服务)的一部分的com.sun.security.auth.callback.DialogCallbackHandler 类已被删除。
它在 JDK 8 中就已被弃用。
9. JRE 版本选择将不再可用。过去有两种做法可以实现 JRE 版本选择。第一个是在命令行中使用 -version: 选项。
如果使用该选项,JVM 将中止而不再启动。第二种方式是从 jar 文件的 manifest 。在这种情况下,JDK 9 将忽略该指令,但会正常启动。有人觉得,与改变命令行相比,强迫人们改变 manifest 太麻烦了。请注意,-version 选项(不带后面的冒号和版本号)仍然可以报告你所使用的 Java 运行时版本。
10. 废弃的GC选项已被移除(JEP 214)。 在 JDK 8(JEP 173)中已经弃用了一些详细的 GC 选项和选项组合。
这些将不会被识别,并将导致 JVM 在启动时中止。要注意的选项如下所示
-XX:-UseParNewGC -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-Xincgc
-XX:+CMSIncrementalMode -XX:+UseConcMarkSweepGC
-XX:+CMSIncrementalMode -XX:+UseConcMarkSweepGC -XX:-UseParNewGC
-XX:+UseCMSCompactAtFullCollection
-XX:+CMSFullGCsBeforeCompaction
-XX:+UseCMSCollectionPassing
在 JDK 9 中,concurrent-mark-sweep (iCMS) 的增量模式已被移除,目前的计划是在 JDK 10 中完全删除 CMS 。
命令行选项
这对我来说似乎是一个很大的变化,但到目前为止,我还没有看到它被突出强调过(或有良好的记录文档)。
除了上述 GC 选项之外,在 JVM 如何处理日志记录中有重大改动。JEP 158 引入了统一日志记录系统,并且该系统被用在 GC 日志中,用于提供统一的 GC 日志记录(JEP 271)。这会影响许多常用的 GC 日志记录命令行选项以及其他一些操作。
为了弄清楚发生了什么改动,我从 OpenJDK 源代码(特别是 src/share/vm/runtime/globals.hpp 文件)中提取了相关的行。通过做 diff ,我能够得到所有从 JDK 8 中删除的 -XX 选项以及已添加到 JDK 9 中的所有 -XX 选项的列表。然后,我写了一个脚本,使用一个简单的应用程序依次查看使用在 JDK 8 中支持但在 JDK 9 中已删除的选项的结果。
对 -XX 选项的更改可以分为三组:
已忽略的命令行选项
如果使用其中的一个,您将收到以下警告消息,但 JVM 将正常启动。
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option <Option>; support was removed in 9.0
AdaptiveSizePausePolicy (自适应大小暂停策略)
CodeCacheMinimumFreeSpace(代码缓存最小空闲空间)
DefaultThreadPriority(默认线程优先级)
JNIDetachReleasesMonitors(JNI分离释放监视器)
LazyBootClassLoader(惰性类启动器)
NmethodSweepCheckInterval(N方法扫描检查间隔)
NmethodSweepFraction(N方法扫描片段)
PrintOopAddress(打印OOP地址)
ReflectionWrapResolutionErrors(反射包装处理错误)
StarvationMonitorInterval(饥饿监视间隔)
ThreadSafetyMargin(线程安全边界)
UseAltSigs(使用alt信号)
UseBoundThreads(使用绑定线程)
UseCompilerSafepoints(使用编译器安全点)
UseFastAccessorMethods(使用快速访问方法)
UseFastEmptyMethods(使用快速空方法)
BackEdgeThreshold(后边缘阈值)
PreInflateSpin(预浸旋转)
已弃用的命令行选项
这些选项将在 JVM 启动时触发警告,或者告诉你 JVM 将使用哪个选项,或者告诉你应该使用哪个选项。其中大部分与统一的 GC 日志有关。
以下是两个关于警告信息的示例:
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.
下表给出了 JDK 8 中的命令行选项及其在 JDK 9 中对应的替换选项。
不再生效的选项
这些是涉及兼容性的最重要的选项。如果你以前使用过这些命令,并且在不更改命令行情况下,那么你的应用程序将无法启动。当你使用其中之一时,你将收到以下错误消息:
Unrecognized VM option '<Option>'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
以下是在 JDK 9 中不再工作的 50 个选项列表。检查下你是否在命令行和启动脚本中使用了这些选项。
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
希望在你移植到 JDK 9 之时,这可以帮助你检查是否需要对应用程序及其配置进行更改。