作者:京东零售刘一达

前语

2006年之后SUN公司决定将JDK进行开源,从此成立了OpenJDK安排进行JDK代码办理。任何人都能够获取该源码,并经过源码构建一个发行版发布到网络上。可是需求一个安排审阅来保证构建的发行版是有用的, 这个安排便是JCP(Java Community Process)。2009年,SUN公司被Oracle公司”白嫖”(参阅2018年Google赔款),此刻咱们运用的JDK通常都是Oracle公司的OpenJDK构建版别-OracleJDK。可是,Oracle公司是一个显着只讲商业而不管情怀的公司,接手Java商标之后,显着加快了JDK的发布版别。2018年9月25日,JDK11成功发布,这是一个LTS版别,包含了17个JEP的更新。与此一同,Oracle把JDK11起以往的商业特性悉数开源给OpenJDK(例如:ZGC和Flight Recorder)。依据Oracle的官方说法(Oracle JDK Releases for Java 11 and Later),从JDK11之后,OracleJDK与OpenJDK的功用根本一致。然后,Oracle宣告今后将会一同发行两款JDK:1. 一个是以GPLv2+CE协议下,由Oracle发行OpenJDK(简称为Oracle OpenJDK);2. 另一个是在OTN协议下的传统OracleJDK。这两个JDK共享绝大多数源码,中心差异在于前者能够免费在开发、测验和出产环境下运用,可是只要半年时刻的更新支撑。后者各个人能够免费运用,可是出产环境中商用就有必要付费,能够有三年时刻的更新支撑。
2021年9月14日,Oracle JDK17发布,现在也是最新的Java LTS版别。有意思的是,Oracle居然”朝令夕改”,OracleJDK17居然是免费的开源协议,并支撑长达8年的保护方案。现在公司内部运用的OracleJDK8最高版别为1.8.0.192,而Oracle在JDK8上开源协议支撑的最高免费版别为jdk1.8.0_202。2022年Spring6和SpringBoot3相继推出,而支撑的最低版别为JDK17。综上所述,JDK8为现在绝大多数以安稳性为主的体系榜首挑选,可是晋级到高版别JDK也仅仅时刻问题。下面图表展现了JDk8到JDK17的每个版别晋级的JEP个数。

JDK8到JDK17有哪些吸引人的新特性?

经过以上图表,咱们能够得出结论,JDK8到JDK17包含很多新特性,为Oracle在Java近5年来的智慧结晶。现在市面上的公司仍是只要少数体系会挑选JDK11或许JDK17作为线上技能选型,假如挑选从JDK8晋级到JDK17必然会有十分大的挑战和较多需求填的坑。本文首要介绍JDK8到JDk17近200个JEP中比较有价值的新特性(依照价值从高到低排序),这儿有一部分特性作者也在线上环境运用过,也会将其间的运用心得共享给咱们。

中心JEP功用及原理介绍

一、Java渠道模块化体系(Jigsaw项目)

JDK9最耀眼的新特性便是Java渠道模块化体系(JPMS,Java Platform Module System),经过Jigsaw项目施行。Jigsaw项目是Java发展进程的一个巨大里程碑,Java模块体系对Java体系产生十分深远的影响。与JDK的函数式编程和 Lamda表达式存在实质不同 ,Java模块体系是对整个Java生态体系做出的改动。

一同也是JDK7到JDK9的榜首跳票王项目。Jigsaw项目本方案于在2010年伴跟着JDK7发布,跟着Sun公司的衰败及Oracle公司的接手,Jigsaw项目从JDK7一直跳票到JDK9才发布。前后阅历了前后将近10年的时刻。即便在2017JDK9发布前夕,Jigsaw项目仍是差点胎死腹中。原因是以IBM和Redhat为首的13家企业在JCP委员会上一手否决了Jigsaw项目作为Java模块化标准进入JDK9发布规模的规划。原因无非便是IBM期望为自己的OSGI技能在Java模块化标准中争夺一席之地。可是Oracle公司没有任何的让步,不惜向JCP发去公开信,宣称假如Jigsaw提案无法经过,那么Oracle将直接自己开发带有Jigsaw项目的java新版别。阅历了前后6次投票,最终JDK9仍是带着Jigsaw项目最终发布了。可是,令人失望的是,Java模块化标准中仍是给Maven、Gradle和OSGI等项目保存了一席之地。关于用户来说,想要完结完好模块化项目,有必要运用多个技能相互合作,仍是添加了杂乱性。假如咱们想要对模块化技能有更多深化了解,引荐阅览书本《Java9模块化开发:中心准则与实践》

1、什么是Java模块化?

简略了解,Java模块化便是将现在多个包(package)组成一个封装体,这个封装体有它的逻辑意义 ,一同也存在具体实例。一同模块遵循以下三个中心准则:

  1. 强封装性:一个模块能够挑选性的对其他模块躲藏部分完结细节。
  2. 界说良好的接口:一个模块只要封装是不够的,还要经过对外露出接口与其他模块交互。因而,露出的接口有必要有良好的界说。
  3. 显现依靠:一个模块通常需求协同其他模块一同作业,该模块有必要显现的依靠其他模块 ,这些依靠联系一同也是模块界说的一部分。

2、为什么要做模块化?

模块化是分而治之的一个重要实践机制,微服务、OSGI和DDD都能够看到模块化思维的影子。现在许多大型的Java项目都是经过maven或许gradle进行版别办理和项目构建,模块的概念在Maven和gradle中早就存在,两者的不同下文也会提到。现在让咱们一同回顾一下现在在运用JDK建立杂乱项目时遇到的一些问题:

2.1 怎样使得Java SE运用程序愈加轻量级的布置?

java包的实质只不过是类的限定名。jar包的实质便是将一组类组合到一同。一旦将多个Jar包放入ClassPath,最终得到只不过是一大堆文件罢了。怎样保护这么巨大的文件结构?现在最有用的办法,也是只能依靠mave或许gradle等项目构建东西。那最底层的Java渠道的Jar包怎样保护?假如我仅仅想布置一个简答的 helloworld运用,我需求一个JRE和一个用户编译的Jar包,并将这个Jar包放到classpath中去。JDK9以前,JRE的运转依靠咱们的中心java类库-rt.jar。rt.jar是一个开箱即用的全量java类库,要么不运用,要么运用悉数。直到JDK8,rt.jar的巨细为60M,跟着JDK的持续发展,这个包必然会越来越大。而且全量的java类库,给JRE也带来了额定的功用损耗。Java运用程序假如能挑选性的加载rt.jar中的文件该多好?

2.2 在露出的JAR包中,怎样躲藏部分API和类型?

在运用Dubbo等RPC结构中,provider需求供给调用的接口界说Jar包,在该Jar包中包含一个共该Jar包内部运用的常量聚合类Constannt,放在constant包内。怎样才干露出JAR包的一同,躲藏常量聚合类Constant?

2.3 一直遭受NoClassDefFoundError的折磨

经过什么办法,能够知道一个Jar包依靠了哪些其他的 Jar包?JDK自身现在没有供给,能够经过Maven东西完结。那为什么不让Java渠道自身就供给这些功用?

3、JPMS怎样处理现有问题?

JPMS具有两个重要的方针:

  1. 强封装(Strong encapsulation) : 每一个模块都能够声明晰哪些包是对外露出的,java编译和运转时就能够施行这些规矩来保证外部模块无法运用内部类型。
  2. 可靠装备(Reliable configuration) :每一模块都声明晰哪些是它所需的,那么在运转时就能够检查它所需的一切模块在运用发动运转前是否都有。

Java渠道自身便是有必要要进行模块化改造的杂乱项目,经过Jigsaw项目落地。

3.1 Project Jigsaw

Modular development starts with a modular platform. —Alan Bateman 2016.9

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

模块化开端于模块化渠道 。Project Jigsaw 有如下几个方针:

  1. 可伸缩渠道(Scalable platform) :逐渐从一个巨大的运转时渠道到有有才能缩小到更小的计算机设备。
  2. 安全性和可保护性(Security and maintainability) :更好的安排了渠道代码使得更好保护。躲藏内部API和更明确的接口界说进步了渠道的安全性。
  3. 进步运用程序功用(Improved application performance) :只要有必要的运转时runtimes的更小的渠道能够带来更快的功用。
  4. 更简略的开发体会Easier developer experience:模块体系与模块渠道的结合使得开发者更容易构建运用和库。

对Java渠道进行模块化改造是一个巨大工程,JDK9之前,rt.jar是个巨大的Java运转时类库,大约有60MB左右。JDK9将其拆分红90个模块左右 ,如下图所示(图片来源《Java 9模块化开发》):

JDK8到JDK17有哪些吸引人的新特性?

4 创立榜首个Java模块

创立一个Java模块其实十分的简略。在现在Maven结构的项目下,只需求在java目录下,新建一个module-info.java文件即可。此刻,当前目录就变成了一个Java模块及Maven模块。

--moudule1
---src
----main
-----java
------com.company.package1
------moudule-info.java
---pom.xml

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

5 模块化对现有运用的影响

5.1 你能够不必可是不能不懂

Java模块化现在并没有展现出其宣传上的影响,一同也鲜有类库正在做模块化的改造。乃至,自己在创立榜首个模块的时分,就遇到了Lombook失效、深度反射失利、Spring发动失利以及无法动态布置的影响。因而,尽量不要测验在线上环境运用模块化技能!不必,可是不代表你能够不懂!跟着Java渠道模块化的完结,运转在JDK9环境的Java程序就现已面临着Jar包和模块的协作问题。未雨绸缪,在发现问题的时分,模块化技能能够帮你快速的定位问题并处理问题。
例如,在从JDK8晋级到JDK11时,咱们经常会收到一下警告:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.jd.jsf.java.util.GregorianCalendar_$$_Template_1798100948_0 (file:/home/export/App/deliveryorder.jd.com/WEB-INF/lib/jsf-1.7.2.jar) to field java.util.Calendar.fields
WARNING: Please consider reporting this to the maintainers of com.jd.jsf.java.util.GregorianCalendar_$$_Template_1798100948_0
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

经过反射拜访JDK模块内类的私有办法或属性,且当前模块并未敞开指定类用于反射拜访,就会呈现以上告警。处理办法也有必要运用模块化相关常识,能够运用遵循模块化之间的拜访规矩,也能够经过设置 –add-opens java.base/java.lang = ALL-UNNNAMED 损坏模块的封装性办法暂时处理;

5.2 Java模块、Maven模块和OSGI模块的之间的联系。

Java模块化技能,理论上能够从Java底层处理模块和模块之间的模块依靠、多版别、动态布置等问题。 前文所述,在2017JDK9发布前夕,以IBM和Redhat为首的13家企业在JCP委员会上一手否决了Jigsaw项目作为Java模块化标准进入JDK9发布规模的规划。经过许多权衡,Java模块化标准中仍是给Maven、Gradle和OSGI等项目保存了一席之地。现在,能够经过Java模块+Maven模块或许Java模块+OSGI模块的办法构建项目,可惜的是,运用多个技能相互合作,仍是添加了杂乱性。

5.3 模块化对类加载机制的影响

JDK9之后,首要取消了之前的扩展类加载器,这是整理之中,由于自身JRE扩展目录都现已不存在,取而代之的是渠道类加载器。然后,类加载器的双亲派遣模型机制进行了损坏,在子类将类派遣给父类加载之前,会优先将当前类交给当前模块(Moudle)或层(Layer)的类加载器加载。所以会构成如下的类加载模型:

JDK8到JDK17有哪些吸引人的新特性?

一同,在JDK9之后,引进的层(Layer)的概念,在Java程序发动时,会解析当前模块路径中的依靠联系,并构成一个依靠联系图包含在引导层(Bootstrap Layer)中,这个依靠联系图从此开端不再改动。因而,现在动态的新增模块要创立新的层,不同的层之间能够包含相同的模块。会构成如下所示的依靠联系(图片来源《Java 9模块化开发》):

JDK8到JDK17有哪些吸引人的新特性?

综上所述,模块化改造关于运用自界说类加载器进行功用动态变化的程序仍是巨大的,一旦运用模块化,必然会导致这类功用受到巨大影响。当然模块化技能普及还需求很长一段时刻,会晚可是不会不来,提前掌握相关技能仍是很必要。

5.4 总结

下面是Java模块化相关技能的一些中心脑图,能够学习参阅:

JDK8到JDK17有哪些吸引人的新特性?

二、废物收回器的一系列优化措施

2.1、ZGC-新一代废物收回器

JDK11中,最耀眼的新特性便是ZGC废物收回器。作为实验性功用,ZGC的特色包含:

  • GC停顿时刻不会超越10ms。
  • 停顿时刻不会跟着堆的巨细,或许活跃目标的巨细而添加;
  • 相关于G1废物收回器而言,吞吐量下降不超越15%;
  • 支撑Linux/x64、window和mac渠道;
  • 支撑8MB~16TB等级的堆收回。

一同依据openJDK官方的功用测验数据显现(JEP333),ZGC的体现十分的超卓:

  • 在仅重视吞吐量指标下,ZGC超越了G1;
  • 在最大推迟不超越某个设定值(10到100ms)下重视吞吐量,ZGC较G1功用愈加突出。
  • 在仅重视低推迟指标下,ZGC的功用高出G1将近两个数量级。99.9th仅为G1的百分之一。

也正是由于如此,ZGC简直是低推迟大内存服务的福音。话说如此,作者在测验运用ZGC进程中仍是发现一些问题:

  1. 由于整个ZGC周期根本都是并发履行,因而创立新目标的速度与废物收回的速度从一开端就在比赛。假如创立新目标的速度更胜一筹,废物会将堆占满导致部分线程堵塞,直到废物收回完毕。
  2. G1虽然是榜首个基于全局的废物收回器,可是依然存在新生代和老时代的概念。可是从ZGC开端,彻底扔掉了新生代和老时代。可是新生代目标朝生夕灭的特性会给ZGC带来很大的压力。彻底的并发履行,必然会形成必定的吞吐量下降。
  3. 在JDK11,G1废物收回器现在还仅仅实验性的功用,只支撑Linux/x64渠道。后续优化接改善,短时刻内无法更新到JDK11中,所以可能会遇到一些不安稳因素。例如: 1. JDK12支撑并发类卸载功用。2. JDK13将可收回内存从4TB支撑到16TB。3. JDK14进步安稳性的一同,进步功用。4. JDK15从实验特性转变为可出产特性 。所以假如想要运用安稳的ZGC功用,只能晋级到JDK17,横跨一个JDK11LTS版别,一同面接近200个JEP带来的功用更新。
  4. 实践线上出产环境,在订单商品等中心体系测验运用ZGC。可是压测成果显现,在JDK11仍是JDK17都差强人意。当然这并不是代表ZGC自身技能缺点,而是需求依据不同的线上环境做更深度的调优和实践。由于数据保密等原因,这儿没有给咱们展现具体的压测数据,读者能够在各自环境进行不同程度的压测验证。

ZGC的原理介绍需求极大的篇幅,本文不方案对ZGC的底层技能展开大规模讨论。假如咱们想要深化学习,作者引荐书本《新一代废物收回器ZGC设计与完结》、Openjdk官网:ZGC介绍以及《深化了解Java虚拟机第五版》中的一些介绍。

2.2、G1废物收回器相关

总的来讲,得益于多个JEP优化,G1废物收回器无论是在JDK11仍是JDK17都体现出了更强壮的才能。跟着CMS废物收回器的抛弃,以及新生代ZGC的初出茅庐,G1废物收回器毫无疑问成了统筹推迟和吞吐的最佳挑选。经过多次压测成果查询,仅仅简略的进步JDK版别,就能够做到更低的GC时刻、更短的GC间隔以及更少的CPU损耗。

场景 JDK 并发 基线参阅 TPS TPM TP99 TP999 TP9999 MAX CPU
1.8.0_192 20 -Xms12g -Xmx12g -XX:+UseG1GC -XX:ParallelGCThreads=13 -XX:ConcGCThreads=4 1680 97640 10 28 31 32 50.07%
11.0.8 20 -Xms12g -Xmx12g -XX:+UseG1GC -XX:ParallelGCThreads=13 -XX:ConcGCThreads=4 1714 99507 10 23 27 29 49.35%

2.2.1、G1的Full GC从串行改为并行(JEP307)

​ G1废物收回器,在 Mix GC收回废物的速度小于新目标分配的速度时,会产生Full GC。之前,产生Full GC时采用的是Serial Old算法,该算法运用单线程标记-铲除-紧缩算法,废物收回吞吐量较高,可是Stop-The-World时刻变长。JDK10,为了削减G1废物收回器在产生Full GC时对运用形成的影响,Full GC采用并行标记-铲除-紧缩算法。该算法能够经过多线程协作 ,削减Stop-The-World时刻。线程的数量能够由-XX:ParallelGCThreads选项来装备 ,可是这也会影响Young GC和Mixed GC线程数量。

2.2.2、可中止的Mixed-GC(JEP344)

G1废物收回器,经过一种名为CSet的数据结构辅佐完结可预测停顿模型算法。CSet中存储了GC进程中可进行废物收回的Region调集。在本特性之前,CSet一旦被确定,就有必要悉数扫描并履行收回操作,这可能会导致超越预期的废物收回暂停时刻。因而,JEP344针对这种问题进行了优化。Java12 中将把 Cset拆分为强制及可选两部分。有限履行强制部分的CSet,履行完结之后假如存在剩余时刻,则持续处理可选Cset部分,从而让GC暂停时刻更接近预期值。

2.2.3 G1支撑NUMA技能(JEP345)

非一致内存拜访架构(英语:non-uniform memory access,简称NUMA)是一种为多处理器的电脑设计的内存架构,内存拜访时刻取决于内存相关于处理器的方位。在NUMA下,处理器拜访它自己的本地内存的速度比非本地内存(内存位于另一个处理器,或许是处理器之间共享的内存)快一些。ParallelGC在前几年现已开端支撑NUMA技能,并且关于废物收回器功用有较大进步。可惜的是,G1废物收回器在JDK14之前一直不支撑此项技能,现在能够经过参数+XX:+UseNUMA在运用G1废物收回器时运用NUMA技能。

2.3、抛弃CMS废物收回器

CMS废物收回器在JDK9彻底被抛弃,在JDK12直接被删除。现在,G1废物收回器是替代CMS的最优挑选之一。

2.4、抛弃ParallelScavenge + SerialOld 废物收回器组合

Java废物收回器有多种多样的组合和运用办法。下面这张图,我大约看过不差10遍,可是每次成果也是相同,记不住!!!!

JDK8到JDK17有哪些吸引人的新特性?

默许废物收回器是哪些?
-XX:+UseParallelGC -XX:-UseParallelOldGC -XX:+UseParallelGC -XX:+UseParNewGC 这几个参数有什么区别?
CMS废物收回器有哪些关键参数?起浮废物怎样处理?怎样防止Full GC产生?
好消息!这些今后都不必回忆了,咱们只需求专注攻克三款废物收回器原理:默许大哥G1、新晋新星ZGC、非亲儿子Shanondoah(了解)。这儿或许有人会抬杠,小内存CMS会有更好的体现。ParNew依然是高吞吐服务的首选。大道至简,简略易用才是王道。G1和ZGC必定是今后JVM废物收回器的重点发展方向,与其耗费精力回忆即将筛选的技能,不如利出一孔,精通一门!

2.4、Epsilon:低开支废物收回器

Epsilon 废物收回器的方针是开发一个控制内存分配,可是不履行任何实践的废物收回作业。下面是该废物收回器的几个运用场景:功用测验、内存压力测验、极度时刻短 job 使命、推迟改善、吞吐改善。

三、诊断和监控相关优化

3.1 Java Flight Recorder[JEP328]

Java Flight Recorder (JFR) 从正在运转的 Java 运用程序搜集诊断和剖析数据。 依据SPECjbb2015基准压测成果显现,JFR 对正在运转的 Java 运用程序的功用影响低于1%。 关于JFR的统计数据,能够运用 Java Mission Control (JMC) 和其他东西剖析。 JFR 和 JMC 在 JDK 8 中是商业付费功用,而在 JDK11 中都是免费开源的。

3.2 Java Mission Control [JMS]

Java Mission Control (JMC) 能够剖析并展现 Java Flight Recorder (JFR) 搜集的数据,并且在 JDK 11 中是开源的。除了有关正在运转的运用程序的一般信息外,JMC 还允许用户深化了解数据。 JFR 和 JMC 可用于诊断运转时问题,例如内存走漏、GC 开支、热点办法、线程瓶颈和堵塞 I/O。JMC能够作为现有JVM监控东西的一个补充,做到维度更多,监控愈加实时(秒级),能从多个视角监控当前JVM进程的功用,愈加更快速的定位并处理问题。

3.3 一致 JVM 日志(JEP158)

在以往的低版别中很难知道导致JVM功用问题和导致JVM溃散的根本原因。不同的JVM对日志的运用是不同的机制和规矩,这就使得JVM难以进行调试。
处理这个问题最佳的办法:对一切的JVM组件引进一个一致的日志结构,这些JVM组件支撑细粒度的和易装备的JVM日志。JDK8以前常用的打印GC日志办法:

-Xloggc:/export/Logs/gc.log //输出GC日志到指定文件
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

3.3.1 方针:

  1. 一切日志记录的通用命令行选项。
  2. 经过tag对日志进行分类,例如:compiler, gc, classload, metaspace, svc, jfr等。一条日志可能会含有多个 tag
  3. 日志包含多个日志等级:error, warning, info, debug, trace, develop。
  4. 能够将日志重定向到控制台或许文件。
  5. error, warning等级的日志重定向到标准过错stderr.
  6. 能够依据日志巨细或许文件数对日志文件进行翻滚。
  7. 一次只打印一行日志,日志之间无穿插。
  8. 日志包含装修器,默许的装修器包含:uptime, level, tags,且装修可装备。

3.3.2 怎样运用

-Xlog[:option]
    option         :=  [][:[][:[][:]]]
                       'help'
                       'disable'
    what           :=  [,...]
    selector       :=  [*][=]
    tag-set        :=  [+...]
                       'all'
    tag            :=  name of tag
    level          :=  trace
                       debug
                       info
                       warning
                       error
    output         :=  'stderr'
                       'stdout'
                       [file=]
    decorators     :=  [,...]
                       'none'
    decorator      :=  time
                       uptime
                       timemillis
                       uptimemillis
                       timenanos
                       uptimenanos
                       pid
                       tid
                       level
                       tags
    output-options :=  [,...]
    output-option  :=  filecount=
                       filesize=
                       parameter=value
  1. 能够经过装备-Xlog:help参数,获取常用的JVM日志装备办法。

  2. 能够经过-Xlog:disable参数关闭JVM日志。

  3. 默许的JVM日志装备如下:

    -Xlog:all=warning:stderr:uptime,level,tags
        - 默许装备
        - 'all' 即是包含一切tag
        - 默许日志输出等级warning,方位stderr
        - 包含uptime,level,tags三个装修
    

    JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

  4. 能够参阅运用如下装备:

    JDK9之前参数-XX:+PrintGCDetails可参阅:

    -Xlog:safepoint,classhisto*=trace,age*,gc*=info:file=/export/Logs/gc-%t.log:time,tid,level,tags:filecount=5,filesize=50MB
       - safepoint表明打印用户线程并发及暂停履行时刻
       - classhisto表明full gc时打印堆快照信息
       - age*,gc* 表明打印包含gc及其细分进程日志,日志等级info,文件:/export/Logs/gc.log。
        - 日志格式包含装修符:time,tids,level,tags
        - default output of all messages at level 'warning' to 'stderr'
        will still be in effect
        - 保存日志个数5个,每个日志50M巨细
    

    JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

    检查GC前后堆、办法区可用容量变化,在JDK9之前,能够运用-XX::+PrintGeapAtGC,现在可参阅:

    -Xlog:gc+heap=debug:file=/export/Logs/gc.log:time,tids,level,tags:filecount=5,filesize=1M
        - 打印包含gc及其细分进程日志,日志等级info,文件:/export/Logs/gc.log。
        - 日志格式包含装修符:time,tids,level,tags
        - default output of all messages at level 'warning' to 'stderr'
        will still be in effect
        - 保存日志个数5个,每个日志1M巨细
    

    JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

JDK9之前的GC日志:

2014-12-10T11:13:09.597+0800: 66955.317: [GC concurrent-root-region-scan-start]
2014-12-10T11:13:09.597+0800: 66955.318: Total time for which application threads were stopped: 0.0655753 seconds
2014-12-10T11:13:09.610+0800: 66955.330: Application time: 0.0127071 seconds
2014-12-10T11:13:09.614+0800: 66955.335: Total time for which application threads were stopped: 0.0043882 seconds
2014-12-10T11:13:09.625+0800: 66955.346: [GC concurrent-root-region-scan-end, 0.0281351 secs]
2014-12-10T11:13:09.625+0800: 66955.346: [GC concurrent-mark-start]
2014-12-10T11:13:09.645+0800: 66955.365: Application time: 0.0306801 seconds
2014-12-10T11:13:09.651+0800: 66955.371: Total time for which application threads were stopped: 0.0061326 seconds
2014-12-10T11:13:10.212+0800: 66955.933: [GC concurrent-mark-end, 0.5871129 secs]
2014-12-10T11:13:10.212+0800: 66955.933: Application time: 0.5613792 seconds
2014-12-10T11:13:10.215+0800: 66955.935: [GC remark 66955.936: [GC ref-proc, 0.0235275 secs], 0.0320865 secs]

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

JDK9一致日志结构输出的日志格式 :

[2021-02-09T21:12:50.870+0800][258][info][gc] Using G1
[2021-02-09T21:12:51.751+0800][365][info][gc] GC(0) Pause Young (Concurrent Start) (Metadata GC Threshold) 60M->5M(4096M) 7.689ms
[2021-02-09T21:12:51.751+0800][283][info][gc] GC(1) Concurrent Cycle
[2021-02-09T21:12:51.755+0800][365][info][gc] GC(1) Pause Remark 13M->13M(4096M) 0.959ms
[2021-02-09T21:12:51.756+0800][365][info][gc] GC(1) Pause Cleanup 13M->13M(4096M) 0.127ms
[2021-02-09T21:12:51.758+0800][283][info][gc] GC(1) Concurrent Cycle 7.208ms
[2021-02-09T21:12:53.232+0800][365][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 197M->15M(4096M) 17.975ms
[2021-02-09T21:12:53.952+0800][365][info][gc] GC(3) Pause Young (Concurrent Start) (GCLocker Initiated GC) 114M->17M(4096M) 15.383ms
[2021-02-09T21:12:53.952+0800][283][info][gc] GC(4) Concurrent Cycle

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

四、愈加高雅的语法或许办法

4.1、调集工厂办法

List,Set 和 Map 接口中,新的静态工厂办法能够创立不可变调集

// 创立只要一个值的可读list,底层不运用数组
static <E> List<E> of(E e1) {
   return new ImmutableCollections.List12<>(e1);
}
// 创立有多个值的可读list,底层运用数组
static <E> List<E> of(E e1, E e2, E e3) {
   return new ImmutableCollections.List12<>(e1, e2,e3);
}
// 创立单例长度为0的Set结合
static <E> Set<E> of() {
   return ImmutableCollections.emptySet();
}
static <E> Set<E> of(E e1) {
   return new ImmutableCollections.Set12<>(e1);
}

4.2、接口私有办法

Java 8, 接口能够有默许办法。Java9之后,能够在接口内完结私有办法完结。

public interface HelloService {
    public void sayHello();
    // 默许办法
    default void saySomething(){
        syaEngHello();
        sayHello();
    };
    // 私有办法
    private void syaEngHello(){
        System.out.println("Hello!");
    }
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

4.3、改善的 Stream API

Java 9 为 Stream 新增了几个办法:dropWhile、takeWhile、ofNullable,为 iterate 办法新增了一个重载办法。

// 循环直到榜首个满足条件后停止
default Stream takeWhile(Predicate predicate);
// 循环直到榜首个满足条件后开端
default Stream dropWhile(Predicate predicate);
// 依据表达式生成迭代器
static  Stream iterate(T seed, Predicate hasNext, UnaryOperator next);
// 运用空值创立空的Stream,防止空指针
static  Stream ofNullable(T t);

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

4.4、JShell

JShell 是 Java 9 新增的一个交互式的编程环境东西。它允许你无需运用类或许办法包装来履行 Java 句子。它与 Python 的解释器相似,能够直接 输入表达式并检查其履行成果。

JDK8到JDK17有哪些吸引人的新特性?

4.5、局部类型揣度(JEP286)

JDK10推出了局部类型揣度功用,能够运用var作为局部变量类型揣度标识符,削减模板代码的生成 ,实质仍是一颗语法糖。一同var关键字的用于与lombok供给的局部类型揣度功用也根本相同。

public static void main(String[] args) throws Exception {
    var lists = List.of("a", "b", "c");
    for (var word : lists) {
        System.out.println(word);
    }
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

​ var关键字只能用于可揣度类型的代码方位,不能运用于办法形式参数,结构函数形式参数,办法回来类型等。标识符var不是关键字,它是一个保存的类型称号。这意味着var用作变量,办法名或则包称号的代码不会受到影响。但var不能作为类或则接口的名字。

​ var关键字的运用的确能够削减许多没必要的代码生成。可是,也存在自己的缺点:1. 现在许多IDE都存在主动代码生成的快捷办法,所以使不运用var关键字区别不大。2. 局部类型揣度,不光是编译器在编译时期要揣度,后边保护代码的人也要揣度,会在必定程度上添加了解成本。

4.6、标准Java HTTP Client

运用过Python或许其他语言的HTTP拜访东西的人,都知道JDK供给的HttpURLConnection或许Apache供给的HttpClient有多么的臃肿。简略对比一下。

python 自带的urllib东西:

response=urllib.request.urlopen('https://www.python.org')  #恳求站点获得一个HTTPResponse目标
print(response.read().decode('utf-8'))   #回来网页内容

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

JDK:

HttpURLConnection connection = (HttpURLConnection) new URL("http://localhost:8080/demo/list?name=HTTP").openConnection();
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
log.info("response code : {}", responseCode);
// read response
try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }
} finally {
    connection.disconnect();
}

Apache HttpClient:

CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// 创立Get恳求
HttpGet httpGet = new HttpGet("http://localhost:12345/doGetControllerOne");
// 响应模型
CloseableHttpResponse response = null;
// 由客户端履行(发送)Get恳求
response = httpClient.execute(httpGet);
// 从响应模型中获取响应实体
HttpEntity responseEntity = response.getEntity();
System.out.println("响应状况为:" + response.getStatusLine());

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

Java 9 中引进了标准Http Client API 。并在 Java 10 中进行了更新的。 到了Java11,在前两个版别中进行孵化的一同,Http Client 简直被彻底重写,并且现在彻底支撑异步非堵塞。与此一同它是 Java 在 Reactive-Stream 方面的榜首个出产实践,其间广泛运用了 Java Flow API。

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("http://openjdk.java.net/"))
      .build();
client.sendAsync(request, BodyHandlers.ofString())
      .thenApply(HttpResponse::body)
      .thenAccept(System.out::println)
      .join();

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

4.7、Helpful NullPointerExceptions(JEP358)

跟着流式编程风格的盛行,空指针反常成为了一种比较难定位的BUG。例如:

a.b.c.i = 99;
a[i][j][k] = 99;

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

在之前,咱们只能收到以下反常堆栈信息,然后有必要凭借DEBUG东西查询问题:

Exception in thread "main" java.lang.NullPointerException
    at Prog.main(Prog.java:5)

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

优化后,咱们能够得到愈加高雅的空指针反常提示信息:

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

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

4.8、愈加高雅的instance of 语法(JEP394)

以下代码是每个Java开发工程师的一块心病:

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

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

上面的instanc of语法一共做了三件事:

  1. 判别是否为String类型;
  2. 假如是,转成String类型;
  3. 创立一个名为s的暂时变量;
    在JDK16中,运用形式匹配思维改善了instance of 用法,能够做到以下优化作用:
if (obj instanceof String s) {// obj是否为String类型,假如是创立暂时变量s
    // Let pattern matching do the work!
    ...
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

咱们能够看到,整体代码风格的确高雅了许多。变量s的作用域为满足条件的判别条件规模之内。因而,以下运用也是合法的:

if (obj instanceof String s && s.length() > 5) {// 由于&&具有短路功用
    flag = s.contains("jdk");
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

可是以下用法,则会报错:

if (obj instanceof String s || s.length() > 5) {    // Error!
    ...
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

合理运用,则能够达到以下作用:

// 优化运用前
public final boolean equals(Object o) {
    if (!(o instanceof Point))
        return false;
    Point other = (Point) o;
    return x == other.x
        && y == other.y;
}
// 优化运用后:
public final boolean equals(Object o) {
    return (o instanceof Point other)
        && x == other.x
        && y == other.y;
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

4.9、愈加高雅的Switch用法

Java里有一句名言:能够用switch结构完结的程序都能够运用if句子来完结。而且Swtich语法在某些工程师眼里,根本没有if句子简洁。JDK14中供给了愈加高雅的swtich语法,例如:

// 之前
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);
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

还能够把switch句子当成一个表达式来处理:

T result = switch (arg) {
    case L1 -> e1;
    case L2 -> e2;
    default -> e3;
};
static void howMany(int k) {
    System.out.println(
        switch (k) {
            case  1 -> "one";
            case  2 -> "two";
            default -> "many";
        }
    );
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

还能够配合关键字yield,在杂乱处理场景里,回来指定值:

int j = switch (day) {
    case MONDAY  -> 0;
    case TUESDAY -> 1;
    default      -> {
        int k = day.toString().length();
        int result = f(k);
        yield result;
    }
};

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

还有吗?其实在JDK17中,还提出了Swtich 形式匹配的预览功用,能够做到更高雅的条件判别:

// 优化前
static String formatter(Object o) {
    String formatted = "unknown";
    if (o instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (o instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (o instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (o instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}
// 优化后
static String formatterPatternSwitch(Object o) {
    return switch (o) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        default        -> o.toString();
    };
}

JDK8到JDK17有哪些吸引人的新特性?JDK8到JDK17有哪些吸引人的新特性?

五、字符串紧缩-Compact Strings(JEP254)

字符串是咱们日常编程中运用最频频的根本数据类型之一。现在,字符串类底层都运用了一个字符数组来完结,每个字符运用2个字节(16位)空间。实践上,很多的字符都属于Latin-1字符规模内,咱们只需求一个字节就能存储这些数据,因而这儿有巨大的可紧缩空间;SPECjbb2005压测成果显现关于GC时刻及GC时刻间隔都有必定程度的进步。具体原理文档也能够参阅【Oracle对CompackStrings共享】

六、 Java Flow API

Reactive Streams是一套非堵塞背压的异步数据流处理标准。从Java9开端,Java原生支撑Reactive Streams编程标准。Java Flow API是对Reactive Streams编程标准的1比1复刻,一同意味着从Java9开端,JDK自身开端在Reactive Streams方向上进行逐步改造。

七、新一代JIT编译器 Graal

即时编译器在进步JVM功用上扮演着十分重要的角色。现在存在两JIT编译器:编译速度较快但对编译后的代码优化较低的C1编译器;编译速度较慢但编译后的代码优化较高的C2编译器。两个编译器在服务端程序及分层编译算法中扮演着十分重要的角色。可是,C2编译器现已存在将近20年了,其间紊乱的代码以及部分糟糕的架构使其难以保护。JDK10推出了新一代JIT编译器Graal(JEP317)。Graal作为C2的继任者呈现,彻底基于Java完结。Graal编译器学习了C2编译器优异的思维一同,运用了新的架构。这让Graal在功用上很快追平了C2,并且在某些特殊的场景下还有更优异的体现。惋惜的是,Graal编译器在JDK10中被引进,可是在JDK17(JEP410)中被废除了,理由是开发者对其运用较少切保护成本太高。开发者也能够经过运用GraalVM来运用Graal编译器;

总结

本文介绍了JDK9-JDK17晋级过的近200个JEP中作者狭隘视点以为价值较高的功用做了一个综述类介绍。首要目的有两个:

  1. 经过本文,咱们能够对即将运用的JDK11及JDK17新特性有一个抽象的了解,期望能够看到一些Java预发最近几年的发展方向。
  2. 经过本文也能够看出,从JDK9到JDK17,Java生态仍是生机勃勃。很多功用的更新意味着更优异的功用及更高效的开发功率,积极主动的测验高版别JDK;
    当然,JDK8到JDK17还有需求优异的新特性,例如:shanondoah废物收回器、Sealed Classes、Records;
    鉴于自己才能有限,文中会呈现一些漏洞,期望咱们找出并纠正,让本文生长为后续JDK17晋级的扫盲手册;

本文正在参加「金石方案」