了解完Java 12 新特性,是否该抛弃java1.7和1.8了?

目录

.
.
.
.
.
.
.
.
.html

概览

Java 12 已如期于 3 月 19 日正式发布,这次更新是 Java 11 这一长期支持版本发布以后的一次常规更新,截至目前,Java 半年为发布周期,而且不会跳票承诺的发布模式,已经成功运行一年多了。经过这样的方式,Java 开发团队可以将一些重要特性尽早的合并到 Java Release 版本中,以便快速获得开发者的反馈,避免出现相似 Java 9 发布时的两次延期的状况。java

Java 12 早在 2018 年 12 月便进入了 Rampdown Phase One 阶段,这意味着该版本全部新的功能特性被冻结,不会再加入更多的 JEP。该阶段将持续大概一个月,主要修复 P1-P3 级错误。主要时间节点以下:web

2018-12-13 Rampdown 第一阶段 ( 从主线分离 )
2019-01-17 Rampdown 第二阶段
2019-02-07 发布候选阶段
2019-03-19 正式发布
本文主要针对 Java 12 中的新特性展开介绍,让您快速了解 Java 12 带来的变化。网络

一个低停顿垃圾收集器(实验阶段) Shenandoah

Java 12 中引入一个新的垃圾收集器:Shenandoah,它是做为一中低停顿时间的垃圾收集器而引入到 Java 12 中的,其工做原理是经过与 Java 应用程序中的执行线程同时运行,用以执行其垃圾收集、内存回收任务,经过这种运行方式,给虚拟机带来短暂的停顿时间。并发

Shenandoah 垃圾回收器是 Red Hat 在 2014 年宣布进行的一项垃圾收集器研究项目,旨在针对 JVM 上的内存收回实现低停顿的需求。该设计将与应用程序线程并发,经过交换 CPU 并发周期和空间以改善停顿时间,使得垃圾回收器执行线程可以在 Java 线程运行时进行堆压缩,而且标记和整理可以同时进行,所以避免了在大多数 JVM 垃圾收集器中所遇到的问题。svg

据 Red Hat 研发 Shenandoah 团队对外宣称,Shenandoah 垃圾回收器的暂停时间与堆大小无关,这意味着不管将堆设置为 200 MB 仍是 200 GB,都将拥有一致的系统暂停时间,不过实际使用性能将取决于实际工做堆的大小和工做负载。性能

图 1. Shenandoah GC 工做周期以下所示
在这里插入图片描述
上图对应工做周期以下:测试

Init Mark 启动并发标记 阶段
并发标记遍历堆阶段
并发标记完成阶段
并发整理回收无活动区域阶段
并发 Evacuation 整理内存区域阶段
Init Update Refs 更新引用初始化 阶段
并发更新引用阶段
Final Update Refs 完成引用更新阶段
并发回收无引用区域阶段
须要了解不是惟有 GC 停顿可能致使常规应用程序响应时间比较长。具备较长的 GC 停顿时间会致使系统响应慢的问题,但响应时间慢并不是必定是 GC 停顿时间长致使的,队列延迟、网络延迟、其余依赖服务延迟和操做提供调度程序抖动等均可能致使响应变慢。使用 Shenandoah 时须要全面了解系统运行状况,综合分析系统响应时间。各类 GC 工做负载对好比下所示:
图 2. 各类 GC 工做负载对比
在这里插入图片描述
下面推荐几个配置或调试 Shenandoah 的 JVM 参数:优化

  • -XX:+AlwaysPreTouch:使用全部可用的内存分页,减小系统运行停顿,为避免运行时性能损失。
  • -Xmx == -Xmsv:设置初始堆大小与最大值一致,能够减轻伸缩堆大小带来的压力,与 AlwaysPreTouch 参数配合使用,在启动时提交全部内存,避免在最终使用中出现系统停顿。
  • -XX:+ UseTransparentHugePages:可以大大提升大堆的性能,同时建议在 Linux 上使用时将 /sys/kernel/mm/transparent_hugepage/enabled 和
    /sys/kernel/mm/transparent_hugepage/defragv 设置为:madvise,同时与
    AlwaysPreTouch 一块儿使用时,init 和 shutdownv 速度会更快,由于它将使用更大的页面进行预处理。
  • -XX:+UseNUMA:虽然 Shenandoah 还没有明确支持 NUMA(Non-Uniform Memory Access),但最好启用此功能以在多插槽主机上启用 NUMA 交错。与 AlwaysPreTouch
    相结合,它提供了比默认配置更好的性能。
  • -XX:+DisableExplicitGC:忽略代码中的 System.gc() 调用。当用户在代码中调用 System.gc() 时会强制 Shenandoah 执行 STW Full GC ,应禁用它以防止执行此操做,另外还可使用
    -XX:+ExplicitGCInvokesConcurrent,在 调用 System.gc() 时执行 CMS GC 而不是 Full GC,建议在有 System.gc() 调用的状况下使用。

不过目前 Shenandoah 垃圾回收器还被标记为实验项目,须要使用参数:- XX:+UnlockExperimentalVMOptions 启用。更多有关如何配置、调试 Shenandoah 的信息,请参阅 henandoah wikiui

增长一套微基准套件

Java 12 中添加一套新的基本的微基准测试套件,该套微基准测试套件基于 JMH(Java Microbenchmark Harness),使开发人员能够轻松运行现有的微基准测试并建立新的基准测试,其目标在于提供一个稳定且优化过的基准,其中包括将近 100 个基准测试的初始集合,而且可以轻松添加新基准、更新基准测试和提升查找已有基准测试的便利性。

微基准套件与 JDK 源代码位于同一个目录中,而且在构建后将生成单个 Jar 文件。但它是一个单独的项目,在支持构建期间不会执行,以方便开发人员和其余对构建微基准套件不感兴趣的人在构建时花费比较少的构建时间。

要构建微基准套件,用户须要运行命令:make build-microbenchmark,相似的命令还有:make test TEST=“micro:java.lang.invoke” 将使用默认设置运行 java.lang.invoke 相关的微基准测试。关于配置本地环境能够参照文档 docs/testing.md|html。

Switch 表达式扩展(预览功能)

Java 11 以及以前 Java 版本中的 Switch 语句是按照相似 C、C++ 这样的语言来设计的,在默认状况下支持 fall-through 语法。虽然这种传统的控制流一般用于编写低级代码,但 Switch 控制语句一般运用在高级别语言环境下的,所以其容易出错性掩盖其灵活性。

在 Java 12 中从新拓展了 Switch 让它具有了新的能力,经过扩展示有的 Switch 语句,可将其做为加强版的 Switch 语句或称为 "Switch 表达式"来写出更加简化的代码。

Switch 表达式也是做为预览语言功能的第一个语言改动被引入新版 Java 中来的,预览语言功能的想法是在 2018 年初被引入 Java 中的,本质上讲,这是一种引入新特性的测试版的方法。经过这种方式,可以根据用户反馈进行升级、更改,在极端状况下,若是没有被很好的接纳,则能够彻底删除该功能。预览功能的关键在于它们没有被包含在 Java SE 规范中。

在 Java 11 以及以前版本中传统形式的 Switch 语句写法以下:

清单 1. Switch 语句示例

int dayNumber;
switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        dayNumber = 6;
        break;
    case TUESDAY:
        dayNumber = 7;
        break;
    case THURSDAY:
    case SATURDAY:
        dayNumber = 8;
        break;
    case WEDNESDAY:
        dayNumber = 9;
        break;
    default:
        throw new IllegalStateException("Huh? " + day);
}

上面代码中多处出现 break 语句,显得代码比较冗余,同时若是某处漏写一段 break 语句,将致使程序一直向下穿透执行的逻辑错误,出现异常结果,同时这种写法比较繁琐,也容易出问题。

换作 Java 12 中的 Switch 表达式,上述语句写法以下:

清单 2. Switch 表达式示例

int dayNumber = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
    default                      -> throw new IllegalStateException("Huh? " + day);
}

使用 Java 12 中 Switch 表达式的写法,省去了 break 语句,避免了因少些 break 而出错,同时将多个 case 合并到一行,显得简洁、清晰也更加优雅的表达逻辑分支,其具体写法就是将以前的 case 语句表成了:case L ->,即若是条件匹配 case L,则执行 标签右侧的代码 ,同时标签右侧的代码段只能是表达式、代码块或 throw 语句。为了保持兼容性,case 条件语句中依然可使用字符 : ,这时 fall-through 规则依然有效的,即不能省略原有的 break 语句,可是同一个 Switch 结构里不能混用 -> 和 : ,不然会有编译错误。而且简化后的 Switch 代码块中定义的局部变量,其做用域就限制在代码块中,而不是蔓延到整个 Switch 结构,也不用根据不一样的判断条件来给变量赋值。

Java 11 以及以前版本中,Switch 表达式支持下面类型: byte、char、short、int、Byte、Character、Short、Integer、enum、tring,在将来的某个 Java 版本有可能会容许支持 float、double 和 long (以及上面类型的封装类型)。

引入 JVM 常量 API

Java 12 中引入 JVM 常量 API,用来更容易地对关键类文件 (key class-file) 和运行时构件(artefact)的名义描述 (nominal description) 进行建模,特别是对那些从常量池加载的常量,这是一项很是技术性的变化,可以以更简单、标准的方式处理可加载常量。

此项改进主要在新的 java.lang.invoke.constant 包中定义了一系列基于值的符号引用类型,可以描述每种可加载常量。符号引用以纯粹 nominal 的形式描述可加载常量,与类加载或可访问 性上下文分开。同时有些类能够做为本身的符号引用(例如 String),而对于可连接常量,另外定义了一系列符号引用类型,具体包括: ClassDesc (Class 的可加载常量标称描述符) ,MethodTypeDesc(方法类型常量标称描述符) ,MethodHandleDesc (方法句柄常量标称描述符) 和 DynamicConstantDesc (动态常量标称描述符) ,它们包含描述这些常量的 nominal 信息。

改进 AArch64 实现

Java 12 中将只保留一套 AArch64 实现,删除全部与 arm64 实现相关的代码,只保留 32 位 ARM 端口和 64 位 aarch64 的端口。删除此套实现将容许全部开发人员将目标集中在剩下的这个 64 位 ARM 实现上,消除维护两套端口所需的重复工做。

当前 Java 11 中存在两套 64 位 AArch64 端口,它们主要存在于 src/hotspot/cpu/arm 和 open/src/hotspot/cpu/aarch64 目录中。这两套代码中都实现了 AArch64,Java 12 中将删除目录 open/src/hotspot/cpu/arm 中关于 64-bit 的这套实现,只保留其中有关 32-bit 的实现,余下目录的 open/src/hotspot/cpu/aarch64 代码部分就成了 AArch64 的默认实现。

使用默认类数据共享(CDS)存档

类数据共享机制 (Class Data Sharing ,简称 CDS) ,容许将一组类预处理为共享归档文件,以便在运行时可以进行内存映射以减小 Java 程序的启动时间,当多个 Java 虚拟机(JVM)共享相同的归档文件时,还能够减小动态内存的占用量,同时减小多个虚拟机在同一个物理或虚拟的机器上运行时的资源占用。

自 Java 8 以来,在基本 CDS 功能上进行了许多加强、改进,启用 CDS 后应用的启动时间和内存占用量显着减小。使用 Java 11 早期版本在 64 位 Linux 平台上运行 HelloWorld 进行测试,测试结果显示启动时间缩短有 32 %,同时在其余 64 位平台上,也有相似或更高的启动性能提高。

Java 12 针对 64 位平台下的 JDK 构建过程进行了加强改进,使其默认生成类数据共享(CDS)归档,以进一步达到改进应用程序的启动时间的目的,同时也避免了须要手动运行:-Xshare:dump 的须要,修改后的 JDK 将在 lib/server 目录中保留构建时生成的 CDS 存档。

固然若是须要,也能够添加其余 GC 参数,来调整堆大小等,以得到更优的内存分布状况,同时用户也能够像以前同样建立自定义的 CDS 存档文件。

改善 G1 垃圾收集器,使其可以停止混合集合

G1 是垃圾收集器,设计用于具备大量内存的多处理器机器,提升了垃圾回收效率。该垃圾收集器 设计的主要目标之一是知足用户设置的预期的 JVM 停顿时间,G1 采用一个高级分析引擎来选择在收集期间要处理的工做量,此选择过程的结果是一组称为 GC 回收集的区域。一旦收集器肯定了 GC 回收集 而且 GC 回收、整理工做已经开始,则 G1 收集器必须完成收集集合集的全部区域中的全部活动对象以后才能中止;可是若是收集器选择过大的 GC 回收集,可能会致使 G1 回收器停顿时间超过预期时间。

Java 12 中将把 GC 回收集(混合收集集合)拆分为必需和可选两部分,使 G1 垃圾回收器能停止垃圾回收过程。其中必需处理的部分包括 G1 垃圾收集器不能递增处理的 GC 回收集的部分(如:年轻代),同时也能够包含老年代以提升处理效率。将 GC 回收集拆分为必需和可选部分时,须要为可选 GC 回收集部分维护一些其余数据,这会产生轻微的 CPU 开销,但小于 1 %的变化,同时在 G1 回收器处理 GC 回收集期间,本机内存使用率也可能会增长,使用上述状况只适用于包含可选 GC 回收部分的 GC 混合回收集合。

在 G1 垃圾回收器完成收集须要必需回收的部分以后,便开始收集可选的部分,若是还有时间的话,可是粗粒度的处理,可选部分的处理粒度取决于剩余的时间,一次只能处理可选部分的一个子集区域。在完成可选收集部分的收集后,G1 垃圾回收器能够根据剩余时间决定是否中止收集。若是在处理完 必需处理的 部分后,属于时间不足,总时间花销接近预期时间,G1 垃圾回收器也能够停止可选部分的回收以达到知足预期停顿时间的目标。

加强 G1 垃圾收集器,使其能自动返回未用堆内存给操做系统

上节中介绍了 Java 12 中加强了 G1 垃圾收集器关于混合收集集合的处理策略,这节主要介绍在 Java 12 中同时也对 G1 垃圾回收器进行了改进,使其可以在空闲时自动将 Java 堆内存返还给操做系统,这也是 Java 12 中的另一项重大改进。

目前 Java 11 版本中包含的 G1 垃圾收集器 暂时没法及时将已提交的 Java 堆内存返回给操做系统, G1 垃圾收集器仅在进行完整 GC (Full GC) 或并发处理周期时才能将 Java 堆返回内存。因为 G1 回收器尽量避免完整 GC,而且只触发基于 Java 堆占用和分配活动的并发周期,所以在许多状况下 G 1 垃圾回收器不能回收 Java 堆内存,除非有外部强制执行。

在使用云平台的容器环境中,这种不利之处特别明显。即便在虚拟机不活动,但若是仍然使用其分配的内存资源,哪怕是其中的一小部分,G1 回收器也仍将保留全部已分配的 Java 堆内存。而这将致使用户须要始终为全部资源付费,哪怕是实际并未用到,而云提供商也没法充分利用其硬件。若是在次期间虚拟机可以检测到 Java 堆内存的实际使用状况,并在利用空闲时间自动将 Java 堆内存返还,则二者都将受益。

为了尽量的向操做系统返回空闲内存,G1 垃圾收集器将在应用程序不活动期间按期生成或持续循环检查总体 Java 堆使用状况,以便 G 1 垃圾收集器可以更及时的将 Java 堆中不使用内存部分返还给操做系统。对于长时间处于空闲状态的应用程序,此项改进将使 JVM 的内存利用率更加高效。

若是应用程序为非活动状态,在下面两种状况下,G1 回收器会触发按期垃圾收集:

自上次垃圾回收完成 以来已超过 G1PeriodicGCInterva l 毫秒, 而且此时没有正在进行的垃圾回收任务。若是 G1PeriodicGCInterval 值为零表示禁用快速回收内存的按期垃圾收集。
应用所在主机系统上执行方法 getloadavg(),一分钟内系统返回的平均负载值低于 G1PeriodicGCSystemLoadThreshold。若是 G1PeriodicGCSystemLoadThreshold 值为零,则此条件不生效。
若是不知足上述条件中的任何一个,则取消当期的按期垃圾回收。等一个 G1PeriodicGCInterval 时间周期后,将从新考虑是否执行按期垃圾回收。

G1 按期垃圾收集的类型根据 G1PeriodicGCInvokesConcurrent 参数的值肯定:若是设置值了,G1 垃圾回收器将继续上一个或者启动一个新并发周期;若是没有设置值,则 G1 回收器将执行一个完整的 GC。在每次一次 GC 回收末尾,G1 回收器将调整当前的 Java 堆大小,此时便有可能会将未使用内存返还给操做系统。新的 Java 堆内存大小根据现有配置肯定,具体包括下列配置:- XX:MinHeapFreeRatio、-XX:MaxHeapFreeRatio、-Xms、-Xmx。

默认状况下,G1 回收器在按期垃圾回收期间新启动或继续上一轮并发周期,将最大限度地减小应用程序的中断。若是按期垃圾收集严重影响程序执行,则须要考虑整个系统 CPU 负载,或让用户禁用按期垃圾收集。

结束语

自上次 Java 11 发布后,很快咱们又迎来了 Java 12 版本的更新。Java 12 版本虽然是非 LTS 版本,可是此次更新也带来了很多 JVM、GC 功能加强、改进,本文主要针对其中几个影响重大变化以及主要的特性作了介绍。Java 12 已经来了,还跟得上更新的节奏吗?

本文只是我的的一点思考,仅表明我的观点,不表明做者所在单位的意见,若有不足之处,还望各位读者可以海涵,如能够,但愿读者们可以反馈意见,交流心得,一同进步。

参考资源

参考 JDK 12,查看更多有关 Java 12 的最新信息。
参考 Shenandoah wiki page,查看更多有关 Shenandoah 的最新信息。
参考 Java 11 新特性介绍

做者:李 林锋 来源:https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-12/index.html