技术记录栈

记录点滴:java、datebase、linux、spring、javascript、nginx

2018/12/27

JDK12的功能和新特性

JDK 12 版本实现参考Java SE平台12版本,以Open JDK12的实现,介绍JDK12的功能和新特性。

低暂停的垃圾收集器-Shenandoah

相比以往以低暂停时间为目标的垃圾收集器,jdk 12提供的全新低暂停垃圾收集器-Shenandoah,使用了更大胆的实现方式,jvm垃圾回收期间对象的转移,与java应用程序并行,Shenandoah垃圾收集器的暂停时间与堆大小无关,无论是200 MB的堆空间,还是200 GB大小的堆,暂停时间都将保持一致。

就像那句备受争议的“软件工程中没有银弹(No Silver Bullet)”的说法一样,Shenandoah垃圾收集器也并不适用于所有的场景,如果优先考虑吞吐量或内存占用,Shenandoah垃圾收集器并不是理想选择。Shenandoah垃圾收集器适用于对响应性有要求,和可预测停顿时间的应用程序。Shenandoah垃圾收集器也不能解决,所有关于JVM暂停的问题。因为除了安全时间点(TTSP)之外的其他因素引起的暂停,已超出了JEP的范围。

Shenandoah使用并行的cpu周期和空间来改善暂停时间。Shenandoah通过增加一个指向Java对象的间接指针,使得GC线程能够在Java线程运行时进行堆压缩,标记和压缩为并发执行,因此只需在对线程的栈进行扫描期间,暂停Java线程一段时间,以完成查找并更新对象图的根节点。

当然对于GC算法的选择并不仅限于最新版本的jdk,如Azul Systems的Zing JVM,以及早前jdk版本中发布的,基于彩色指针的低暂停收集器ZGC,并行和并发共存的G1垃圾收集器(G1中对象的转移过程(evacuation)不是并发的),以及使用度极高的CMS并发标记垃圾收集器(暂停时执行年轻代复制,并且从不压缩年老代)。

微基准测试工具Microbenchmark

Microbenchmark作为常规性能测试的一部分,在JDK源代码中添加一组基础的微基准测试,兼容已有的微基准测试(基于[Java Microbenchmark Harness (JMH)]),也可以创建新的基准测试,在Microbenchmark套件中包含大约一百个基准的初始集。

Switch表达式扩展

扩展switch语句,使其可以用作语句或表达式,并且“传统”或“简化”两种都可以使用。这些变化将简化日常编码,在预览版中还提供了模式匹配(JEP 305)。
switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

保留AArch64端口

删除JDK中已有的两个64位ARM端口中的一个(src/hotspot/cpu/arm和open/src/hotspot/cpu/aarch64,两者都是aarch64接口/标准的实现),只保留32位ARM端口和64位aarch64端口,意在消除维护两个端口所做的重复工作,让贡献者将他们的精力集中在单个64位ARM实现上。

默认CDS

在64位平台上使用默认类列表,增强了JDK生成的类共享(CDS),改善了开箱即用的启动时间,取消了用户必需运行-Xshare:dump,才能使用CDS的功能。

可中止的G1垃圾收集器

如果G1垃圾收集器有可能超过预期的暂停时间,则可以使用中止选项。
G1的目标之一是满足用户预期的暂停时间,G1使用高级分析引擎来选择收集期间要完成的工作量(这部分取决于应用程序行为)。它会选择一些区域(regions)做为回收的对象(collection set)。一旦确定了collection set就会开始收集工作,则G1必须在不停止的情况下,对collection set区域中的活动对象进行处理。如果选择了过大的collection set,则有可能导致G1超过预期的暂停时间,需要一种检测机制来发现,何时开始反复选择错误的工作量(收集区域选择过大),如果存在这种现象,则让G1逐步递增地执行收集工作,这种机制将可以让G1在大多的时间里,都能满足预期的暂停时间。

G1归还未使用的内存

增强后的G1垃圾收集器,可以在空闲时自动将Java堆内存返还给操作系统。如果应用程序活动非常低,G1应该在合理的时间段内释放未使用的Java堆内存。为了实现向操作系统返还最大内存量的目标,G1将在应用程序不活动期间,定期尝试触发并发周期以确定整体Java堆使用情况。这将导致它自动将Java堆的未使用部分返回给操作系统。在用户控制下,可以执行完整的GC以最大化返回的内存量(可选)。

JVM常量API

引入API来模拟关键类文件和运行时artifacts的描述,特别是可以从常量池加载的常量。

每一个class文件都对应一个常量池,用来存储类中字节码指令的操作数。从广义上来讲,常量池中的条目(entries)描述了运行时的artifacts,如:类和方法或者String,Integer这种简单的值对象。所有这些条目都称为可加载常量(loadable constants),因为它们都可以作为ldc指令("load constant")的操作数 。它们也可能出现在invokedynamic指令的参数列表中。

执行一个 ldc 或者 invokedynamic 指令,通常是为了把一个 loadable constant 转换成一个java类,如String或int。操作class文件的程序需要对字节码指令进行建模,然后对可加载常量(loadable constants)进行建模,但是,使用标准Java类型来模拟可加载常量是不合适的。

比如:用一个可加载常量CONSTANT_String_info条目来描述一个string,是可以接受的,因为生成一个“live” String对象很简单,但用一个CONSTANT_Class_info来描述一个类则不太现实。因为生成“live”Classobject依赖于类加载的正确性和一致性。而类加载过程又是那么的繁琐。

因此,处理可加载常量的程序如果能够以纯粹的别名形式操作类和方法,以及不太常见的artifacts(如方法句柄和动态计算的常量),则要简单的多:

1、字节码解析和生成libraries需要以符号形式描述类和方法句柄。如果没有标准机制,它们需采用临时机制,无论是描述符类型(如ASM)Handle,还是字符串元组(方法、所有者,方法名称,方法描述符),或者这些编码都是ad-hoc(容易出错)编码到单个字符串。

2、invokedynamic旋转字节码的(如LambdaMetafactory)操作,如果能够在符号域中工作,而不是使用“live”类和方法句柄,则会更简单。

这一版本中定义了一组基于值的符号引用(jvm 5.1)类型java.lang.invoke.constant,描述每种可加载的常量。符号引用描述了以别名形式可加载的常量,有别于类加载。一些类可以作为它们自己的符号引用(例如,String),对于可链接常量,定义了一组符号引用类型(ClassDesc、MethodTypeDesc、MethodHandleDesc和DynamicConstantDesc),它们包含了描述这些常量的名称信息。