JDK14来了:9大重磅特性解读,不容错过!!!

JEP 305: Pattern Matching for instanceof (Preview)
JEP 358: Helpful NullPointerExceptions
JEP 361: Switch Expressions (Standard)
JEP 345: NUMA-Aware Memory Allocation for G1
JEP 349: JFR Event Streaming
JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination
JEP 363: Remove the CMS Garbage Collector
JEP 364: ZGC on macOS
JEP 368: Text Blocks (Second Preview)

JEP 305: Pattern Matching for instanceof (Preview)

很明显这个特性跟使用instanceof有关。平常我们写代码是这样的。很明显这不是最优的方式,怎么看怎么别捏, 代码显得有点冗余乏味,我们既要类型判断,还要类型强转:

if (obj instanceof String) {
    String s = (String) obj;
    // use s
}

那么新的方式是怎么样的呢?请往下看。厉不厉害,牛不牛逼:

if (obj instanceof String s) {
    //todo can use s here
} else {
    //todo can't use s here
}

而且还能用的更复杂一些,需要注意的是,下面这种写法时,必须是&&,而不能是||,为什么有这个限制,我想很容易理解吧:

if (obj instanceof String s && s.contains("afei")) {
    ... ...
}

JEP 358: Helpful NullPointerExceptions

这个特性优点意思,绝对非常有空。想象我们有一行这样的代码,并且在这里抛出了空指针,那么,我们没办法知道空指针是由于a引起的,还是a.b引起的,还是a.b.c引起的:

int index = a.b.c.i ;

所以,我们可能要将代码改造成这样,这样才能在代码抛出NPE时更容易定位问题:

if (a!=null){
    if (a.b!=null){
        if (a.b.c!=null){
            int index = a.b.c.i ;
        }
    }
}

JEP358这个特性就是帮我们解决这个问题的。假设我们的代码还是这样写的:int index = a.b.c.i ,并且由于a.b为null引起的空指针,那么抛出的异常信息是这样的,这个异常就非常友好了吧:

Exception in thread "main" java.lang.NullPointerException: 
        Cannot read field "c" because "a.b" is null
    at Prog.main(Prog.java:5)

数组方式也是一样的,假设有一行这样的代码:int height = a[i][j][k],并且由于a[i][j]为空导致的NPE,那么异常信息是这样的:

Exception in thread "main" java.lang.NullPointerException:
        Cannot load from object array because "a[i][j]" is null
    at Prog.main(Prog.java:5)

JEP 361: Switch Expressions (Standard)

这个特性也是继承自JDK13的JEP 354: Switch Expressions (Preview),有一段switch老语法代码如下:

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}

这段代码显得有点冗余,新的语法代码如下,很明显简练很多:

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);
}

而且,新的switch语法能直接将其作为表达式,用法如下:

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
};

新的switch语法相比以前灵活了很多很多!

JEP 345: NUMA-Aware Memory Allocation for G1

了解这个特性之前,我们需要了解什么是NUMA。NUMA就是非统一内存访问架构(英语:non-uniform memory access,简称NUMA),是一种为多处理器的电脑设计的内存架构,内存访问时间取决于内存相对于处理器的位置。在NUMA下,处理器访问它自己的本地内存的速度比非本地内存(内存位于另一个处理器,或者是处理器之间共享的内存)快一些。如下图所示,Node0中的CPU如果访问Node0中的内存,那就是访问本地内存,如果它访问了Node1中的内存,那就是远程访问,性能较差:

NUMA架构

非统一内存访问架构的特点是:被共享的内存物理上是分布式的,所有这些内存的集合就是全局地址空间。所以处理器访问这些内存的时间是不一样的,显然访问本地内存的速度要比访问全局共享内存或远程访问外地内存要快些。另外,NUMA中内存可能是分层的:本地内存,群内共享内存,全局共享内存。

JEP345希望通过实现NUMA-aware的内存分配,改进G1在大型机上的性能!现代的multi-socket服务器越来越多都有NUMA,意思是,内存到每个socket的距离是不相等的,内存到不同的socket之间的访问是有性能差异的,这个距离越长,延迟就会越大,性能就会越差!(https://openjdk.java.net/jeps/345)。只需要设置JVM参数:+XX:+UseNUMA 后, 当JVM初始化的时候(即Java应用启动的时候),G1的Region集合就会被均匀的分散到所有有效的NUMA节点上。

JEP 349: JFR Event Streaming

Java为了更方便的了解运行的JVM情况,在之前的版本中提供了JFR特性,即JDK Flight Recorder。但是使用不太灵活。虽然JVM通过JFR暴露了超过500项数据,但是其中大部分数据只能通过解析JFR日志文件才能获取得到,而不是实时获取。用户想要使用JFR的数据的话,用户必须先开启JFR进行记录,然后停止记录,再将飞行记录的数据Dump到磁盘上,然后解析这个记录文件。

// 下面这条命令会立即启动JFR并开始使用templayte.jfc的配置收集60s的JVM信息,并输出到output.jfr中。
// 一旦记录完成之后,就可以复制jfr文件到你的工作环境使用jmc GUI来分析。
// 它几乎包含了排查JVM问题需要的所有信息,包括堆dump时的异常信息等。
jcmd <PID> JFR.start name=test duration=60s settings=template.jfc filename=output.jfr

这样对于应用程序分析很有效,但是对于实时监控却并不友好,因为无法将JFR采集的信息实时动态展示到仪表板上。JEP349特性能够通过异步订阅的方式直接获取JFR记录的数据,而不需要分析Dump文件。如下这段代码所示:

try (var rs = new RecordingStream()) {
  rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
  rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
  rs.onEvent("jdk.CPULoad", event -> {
    System.out.println(event.getFloat("machineTotal"));
  });
  rs.onEvent("jdk.JavaMonitorEnter", event -> {
    System.out.println(event.getClass("monitorClass"));
  });
  rs.start();
}

JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination

ParallelScavenge + SerialOld GC的GC组合要被标记为Deprecate了,也就意味着,在接下来的某个JDK版本中,会彻底不兼容这种GC组合。

JDK官方给出将这个GC组合标记为Deprecate的理由是:这个GC组合需要大量的代码维护工作,并且,这个GC组合很少被使用。因为它的使用场景应该是一个很大的Young区配合一个很小的Old区,这样的话,Old区用SerialOldGC去收集时停顿时间我们才能勉强接受。事实上,这种场景很少使用,而且风险即可。总而言之,老年代能用UseParallelOldGC ,还需要什么SerialOldGC,是吧!

JEP 363: Remove the CMS Garbage Collector

该来的总会来,自从G1横空出世后,CMS在JDK9中就被标记为Deprecate了(JEP 291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector),那么CMS被彻底移除也就是一个时间问题了。

基于Region分代是大势所趋,CMS的设计还是落后了一点,而且它的碎片化问题,给你的JVM实例就像埋了一颗炸弹。说不定哪次就在你的业务高峰期来一次FGC,这可是采用Mark—Sweep-Compact算法的SerialOldGC回收,JVM中性能最差的垃圾回收方式,停顿个几秒钟,上十秒都有可能。

当然,如果你JDK14中你还是配置的CMS(-XX:+UseConcMarkSweepGC),JVM不会报错,只是给出一个告警信息,JVM会自动回退以默认GC方式启动JVM:

Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; \
support was removed in <version>
and the VM will continue execution using the default collector.

EP 364: ZGC on macOS

很简单,就是在macOS上支持ZGC,没什么太多需要说明的。

JEP 368: Text Blocks (Second Preview)

这个特性对应JDK13的JEP 355: Text Blocks (Preview),只不过这是Second Preview而已,所以,笔者只简单解决一下这个新的语法。

如果有一段SQL,老的语法是这样写的:

String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" +
               "WHERE `CITY` = 'INDIANAPOLIS'\n" +
               "ORDER BY `EMP_ID`, `LAST_NAME`;\n";

新的语法是这样写的:

String query = """
               SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
               WHERE `CITY` = 'INDIANAPOLIS'
               ORDER BY `EMP_ID`, `LAST_NAME`;
               """;

如果有一段脚本需要执行,老的语法是这样的:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("function hello() {\n" +
                         "    print('\"Hello, world\"');\n" +
                         "}\n" +
                         "\n" +
                         "hello();\n");

而新的语法是这样的:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object obj = engine.eval("""
                         function hello() {
                             print('"Hello, world"');
                         }
                         
                         hello();
                         """);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342