Java14 已于 2020 年 3 月 17 号发布,官方特性解读在这里:
https://openjdk.java.net/projects/jdk/14/
以下是个人对于特性的中文式理解;
原来的 Java 语法 instanceof 判断之后,还需要进行类型的显示转换,才能使用实际类的一些方法,如下所示:
if (obj instanceof String) {
String s = (String) obj;
// use s
}
这里做了一个增强,其实用过 kotlin 的同学就马上明白了,语法基本上类似,不过 kotlin 的实现可能还更加平顺:
if (!(obj instanceof String s)) {
.. s.contains(..) ..
} else {
.. s.contains(..) ..
}
这里主要是提供一种像安装程序的方式类似的安装体验,针对之前可运行的 .jar 文件,提供一种面向不同 os 的打包格式的工具,这个工具可以支持 Windows 下的 msi 和 exe 格式、 linux 下的 dep 和 rpm 格式以及 Mac 下的 pkg 和 dmg 格式,这里除了打包之后有一个通用的系统的安装体验之外,还能将一些 main 函数启动的初始入参固化到打包之后的文件中。
整体打包的命令会根据是否是做了模块化有所区别,分别是:
jpackage --name myapp --input lib --main-jar main.jar
以及
jpackage --name myapp --module-path lib -m myapp
可以通过 --type 来指定打包之后的格式,比如上述命令后面加入 --type dmg ,就是打包出 dmg 格式的安装包;
这个工具的动机主要是之前直接运行jar文件的方式需要使用方关心三个问题:
1、lib 的路径;
2、是否有显性的 main 函数指定和声明;
3、main 函数的入参;
这个工具可以简化 jar 文件使用的门槛;
这个点是基于 G1 垃圾回收器的,主要是针对年轻代的对象内存分配做了一个优化,提高的是 CPU 计算过程中的内存访问速度;
NUMA 是 non-unified memory access 的缩写,主要是指在当前的物理机中,比较普遍是多核的,这样每个核对于每一块或者某一区域的内存访问速度会随着核和物理内存所在的位置的远近而有不同的时延差异(在同一块物理 scoket 上的 cpu 对于不同距离的内存访问基本时延相同),这个就是这个优化的一个大前提;
然后在 Java 中,堆内存分配一般发生在线程运行的时候,new 了一个新对象,然后该线程会触发 G1 去 allocate 一块内存出来,用来存放新的对象,在 G1 中,其实就是一块region(大对象除外,大对象需要多个 region ,但是不在这个优化的范畴中),那其实就在这个 allocation 分配新内存的过程中,优先在当前线程所绑定的同个 NUMA node 内存,因为是基于同一个线程中创建的对象大部分是短存活并且高概率互相调用的,如果没有足够的可分配内存,就会触发一次 YGC 。
这个点的优化,个人感觉性能优化可能比较有限;
在 Hotspot 中,目前已经使用 Java Flight Recorder 实现了 JVM 各类事件的 500 多个埋点,这些事件可以涵盖 JVM 内部的内存分配、类初始化、对象调用、线程运行、类加载器的行为等等;但是之前的这些事件记录是通过开启记录之后存储在一个固定的文件中,目前都是通过 dump 这个文件,然后通过 Java Mission Control 或者 profiler 这些工具来做图示化的展示,做一些离线的分析等;
这个优化主要是将JFR的事件记录形成一个事件流,屏蔽和抽象掉具体事件的打点方式,实现层面只需要声明一个 RecordingStream 对象,订阅方式有如下三种:
// disk repository中读取事件流,不需要dump具体文件
public static EventStream openRepository(Path directory);
// 从具体的record文件流中读入事件
public static EventStream openFile(Path file);
// 基于现实事件发送来做订阅,需要外部来触发事件的发送
public static EventStream openRepository();
然后可以通过 onEvent 来指定需要监听的事件类型,并且编写具体监听到之后的处理实现;
这个特性对于整体 JVM 的运行会带来额外的负载,目标是带来不到 1% 的整体时延影响,但是在 test 报告中未有具体影响百分比,描述是 acceptable ,性能敏感的应用具体使用的时候还需要 cover 下前后的性能影响;
这个优化很直白,就是空指针异常的异常信息优化,具体指明哪个对象或者对象中的哪个参数是null的,这个点对于排查定位问题比较有帮助,特别是代码中有这样的a.b.c.d.e.XXX这样的写法的时候,之前空指针只能说哪一行抛了,但是不能识别到是a或b或c或d空指针了;
例如 a.i = 99,之前的空指针的异常信息如下:
Exception in thread "main" java.lang.NullPointerException
at Prog.main(Prog.java:5)
优化后的空指针异常携带的信息如下:
Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "i" because "a" is null
at Prog.main(Prog.java:5)
这个报错比之前人性化了不少;
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);
}
嗯,很 kotlin 的优化;
这个特性之前在 Java13 的时候出现过,preview,就是个偏实验测试的特性,这次在 14 中,依旧是 preview ,主要是扩展了两个 escape sequence ,分别是 “\” 和 “\s” , “\” 主要是用来明确标明换行的位置及之前的内容,主要是保留行末的空格,如下:
String text = """
Lorem ipsum dolor sit amet, consectetur adipiscing \
elit, sed do eiusmod tempor incididunt ut labore \
et dolore magna aliqua.\
""";
“\s” 就是代表一个空格,有些场景需要用空格补足字符长度到定长,如下所示:
String colors = """
red \s
green\s
blue \s
""";
1、Solaris/SPARC, Solaris/x64, and Linux/SPARC 端口标注过期;
2、年轻代 GC 算法 ParallelScavenge 和老年代回收算法 SerialOld 这个组合标注过期,即在 jvm 启动参数指定 XX:+UseParallelGC -XX:-UseParallelOldGC 会在之后的某个版本不能生效了;
3、CMS 垃圾回收期移除;
4、移除 java.util.jar 包中的 pack200 和 unpack200 这两个 api ,之前在 Java11 已经标注过期,这两个 api 主要是做 jar 包的压缩和解压的;
Java 14 的特性大部分是一些小的优化,下一个 LTS 版本可能是 16 ,预计差不多明年 3,4 月份的样子,现有的最新的 LTS 版本是 Java 11 ,我们现在绝大部分使用的是 Java 8 ,保持持续的关注以及权衡好升级的利益,这个是我们需要从现在开始考虑的~~~
本文作者:
江丹阳,淘系技术部,目前主要专注互动、直播领域。感兴趣的小伙伴可以一起加入我们,简历投递至:
本文缩略图:icon by 金角大王
Tips:
# 点下“在看”❤️
# 然后,公众号对话框内发送“蜘蛛侠”,试试手气?????
# 本期奖品是漫威蜘蛛侠之英雄归来POP公仔(秃鹰)。