为什么JDK17是Java开发者必须掌握的版本?

😇 序言

  作为一位资深JAVA,我很高兴在这里分享我的关于JDK17的看法。作为Java开发人员,我们都知道JDK是Java Development Kit的缩写,是Java程序员必不可少的开发工具包。JDK17是最新的一个版本,于2021年9月14日正式发布,它是JDK的17个主要版本之一。在本篇博客中,我将介绍JDK17的新特性、改进以及对Java开发者的影响,以及如何使用和优化JDK17来提高应用程序的性能稳定性

💥 为什么升级JDK17?

  • 长期支持版本
    JDK17是Oracle于2021年9月14日发布的一个长期支持版本(LTS)。它将获得长期的更新和支持,帮助保持程序的稳定性和可靠性。
  • 性能提升
    经过综合评估,我们发现在从Java 8升级到Java 11后,G1GC的平均速度提升了16.1%,ParallelGC则提升了4.5%。当从Java 11升级到Java 17时,G1GC的平均速度提升了8.66%,而ParallelGC则提升了6.54%。这些结果是基于对OptaPlanner用例的基准测试得出的。总的来说,这些优化让垃圾回收器更加优秀。
    最大的亮点是带来了稳定版的ZGC垃圾回收器,达到亚毫秒级停顿。
  • 新语法和特性
    Switch表达式简化Text Blocks文本块instanceof的模式匹配升级和NullPointerException提示信息改进等
  • 支持最新的技术和框架
    Spring framework6Spring Boot3 都默认使用 Java 17作为最低版本

💯 压测结果

  1、JDK17相对于JDK8和JDK11,所有垃圾回收器的性能都有很明显的提升,特别是稳定版的ZGC垃圾回收器
  2、不论任何机器配置下,都推荐使用ZGC,ZGC的停顿时间达到亚毫秒级,吞吐量也比较高
  我在JDOS平台上选择了不同配置的机器(2C4G、4C8G、8C16G),并分别使用JDK8、JDK11和JDK17进行部署和压测。
  整个压测过程限时60分钟,用180个虚拟用户并发请求一个接口,每次接口请求都创建512Kb的数据。最终产出不同GC回收器的各项指标数据,来分析GC的性能提升效果。

以下是压测的性能情况:


参考官网数据

  OracleJDK和OpenJDK这两个之间没有真正的技术差别,因为针对Oracle JDK构建过程是基于OpenJDK的。自从JDK11开始,OracleJDK和OpenJDK在功能上基本相同,所以推荐使用 OpenJDK17 或其他开源的JDK版本,这些开源版本都是基于OpenJDK构建并提供长期支持的,比如:AdoptOpenJDK、RedHatOpenJDK。

官网参考

💦 新特性

🦄 JVM改进

  1、ZGC垃圾回收器从实验性功能更改为正式产品功能,从JDK11引入以来,经过持续的迭代升级,目前已经足够稳定。需要手动开启,开启方式:-XX:+UseZGC
  2、G1垃圾回收器仍然作为默认垃圾回收器,进行改进升级,主要包括可中止的混合收集集合、NUMA 可识别内存分配等
  3、JDK14开始删除CMS垃圾回收器
  4、JDK14开始弃用 ParallelScavenge 和 SerialOld GC 的组合使用
  5、JDK15禁用偏向锁,默认禁用:-XX:+UseBiasedLocking
  6、NullPointerException 提示信息改进
JDK14以前的出现NullPointerException时,只能定位到所在异常行,无法定位具体是哪个变量。改进后的NullPointerException,可以清晰描述具体变量,提升了空指针异常的可读性。

🐉 新语法特性

🐳 Switch表达式简化

  switch表达式带来了简化式的编码方式,提供了新的分支切换方式,即 -> 符号,右则表达式方法体在执行完分支方法之后,自动结束 switch 分支,同时 -> 右则方法块中可以是表达式、代码块或者是手动抛出的异常
官网参考

传统写法

新写法

🐬 Text Blocks文本块

官网参考
  通过编写"“”,来减少转义字符和换行符,达到简化代码和提高代码可读性的目的

🦋 Record类型

官网参考

  record 是 JDK 14 引入的关键字,用于声明不可变的数据类。它适用于存储纯粹的值类型数据,如接口传输数据、坐标点和只读的日志记录。与 lombok 相比,record 简化了定义纯粹数据类型的过程。由于 record 类是不可变的,成员变量只能设置一次且无法更改,无需提供显式的 setter() 方法。

1、定义Point类,使用关键字record,未定义get/set


2、查看编译后的字节码文件



3、使用Point类

🍁 instanceof 的模式匹配升级

instanceof类型判断再也不需要强制转换
官网参考

🌿 密封的类和接口

官网参考
  JDK15开始,引入了sealed普通类或接口类,这些类只允许被指定的类或者interface进行扩展和实现。

  使用修饰符sealed,您可以将一个类声明为密封类。密封的类使用关键字permits列出可以直接扩展它的类。子类可以是最终的,非密封的或密封的
比较实用的一个特性,可以用来限制类的层次结构

🍄 其他优化和升级

感兴趣的同学,推荐阅读OpenJDK官方文档说明, 从JDK11到17的改动

🌽 升级步骤

🍑 JDK选择

OpenJDK17下载
行云镜像:jdt-base-tomcat/java-jdt-centos7.4-openjdk-17.0.2-tomcat8.0.53

🥬 pom编译配置升级

maven编译所需JDK升级至17

<properties>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

🍓 SpringBoot升级

SpringBoot版本升级到2.7.15,Spring版本升级为5.3.29
为什么不升级到SpringBoot3?
  Spring Boot 3.0最低要求 Java 17,SpringBoot3.0带来了很多变化,和SpringBoot2差异较大。 考虑到公司很多中间件都是基于SpringBoot2构建的,所以此处推荐升级到SpringBoot2的最高版本2.7.15。
POM升级

<parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.7.15</version>
</parent>

也可以通过设置dependencyManagement的方式:

<properties>
    <!-- 框架版本配置-->
    <springboot-version>2.7.15</springboot-version>
    <springframework.version>5.3.29</springframework.version>
</properties>  

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>${springboot-version}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-framework-bom</artifactId>
            <version>${springframework.version}</version>
            <scope>import</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</dependencyManagement>

参考:

spring升级指南
springboot版本官网

循环依赖问题
SpringBoot升级到2.7.15后,如果应用中存在循环依赖的问题,启动时会报如下错误:

原因:官方文档不鼓励循环依赖引用,默认情况下是禁止的

解决方案:

第一种:推荐更新应用中bean的依赖关系来解决
第二种:配置文件中加入以下配置,为了和旧版本保持一致,此配置推荐添加

#放开循环依赖
spring.main.allow-circular-references=true

🥝 常用中间件升级

🧀 Lombok版本升级到1.18.20以上
<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.20</version>
</dependency>

如果不升级,编译时会报错如下:

🌈 swgger问题,springfox3.0.0和springboot2.7版本不兼容

异常:

Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException: 
Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null
🎋 swgger问题,springfox3.0.0和springboot2.7版本不兼容

异常:

Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException: 
Cannot invoke "org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getPatterns()" because "this.condition" is null

解决方案:

/**
 * 增加如下配置可解决Spring Boot 2.7.15 与Swagger 3.0.0 不兼容问题
 **/
@Bean
public BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {

@Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
            }
return bean;
}

private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
            List<T> copy = mappings.stream().filter(mapping -> mapping.getPatternParser() == null).collect(Collectors.toList());
            mappings.clear();
            mappings.addAll(copy);
        }

@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
                Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
            } catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
            }
        }
    };
}

参考

🥗 AKS升级(针对直接从JDK8升级的情况)

异常:Causedby: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
原因:Java11 删除了 Java EE modules,其中就包括 java.xml.bind (JAXB)。

解决方案:
手动引入如下包即可

<!-- API, java.xml.bind module --> 
<dependency>
      <groupId>jakarta.xml.bind</groupId>
      <artifactId>jakarta.xml.bind-api</artifactId>
      <version>2.3.2</version>
</dependency> 
<!-- Runtime, com.sun.xml.bind module -->
<dependency>
       <groupId>org.glassfish.jaxb</groupId>
       <artifactId>jaxb-runtime</artifactId>
       <version>2.3.2</version>
</dependency>
🍔 Concrete配置中心阻塞升级

使用 Concrete时,启动时异常:

 Unable to make field private static final java.lang.reflect.Method jdk.proxy2.$Proxy97.m0 accessible: 
 module jdk.proxy2 does not "opens jdk.proxy2" to unnamed module @61d47554

原因:

分析下Concrete报错的原因,如下图
com.wangyin.concrete.spring.ConcreteConfigProcessor#postProcessAfterInitialization(212行)的实现逻辑


解决方案:

1、在JVM启动参数中设置–add-opens jdk.proxy2来开启私有字段的访问,但因为动态代理生成的包名是随机不明确的,所以这种方案不可行。JDK官方文档也明确表示不支持访问动态代理内部的随机字段。官方说明:https://cr.openjdk.org/~mr/jigsaw/spec/api/java/lang/reflect/Proxy.html

2、代码修改
f.setAccessible(true) 移到 Modifier.isStatic(f.getModifiers()) 的判断下方即可。原因是方法 Modifier.isStatic(f.getModifiers()) 本来就要跳过静态字段,这样修改直接避免了访问。

🦀 JVM启动参数配置

🔱 开启ZGC

启动参数中配置:-XX:+UseZGC
移除-XX:ConcGCThreads,行云部署下JVM参数配置需要清除

🎨不同中间件所需启动参数

升级JDK17后,项目启动时可能会遇到如下两种类型的异常:

1、cannot access class sun.util.calendar.ZoneInfo (in module java.base) because module java.base does not export sun.util.calendar to unnamed module @0x2611f533

2、Unable to make field final int java.math.BigInteger.signum accessible: module java.base does not “opens java.math” to unnamed module @525f1e4e

异常原因:
自从JDK9中引入了模块化功能后,再到JDK17,对于包扫描和反射的权限控制更加的严格。常见的库比如(Spring)大量用到包扫描和反射,所以常出现此错误。

解决方案:
一个粗暴的解决办法是将没开放的module强制对外开放,即保持和Java9之前的版本一致。
–add-exports导出包,意味着其中的所有公共类型和成员都可以在编译和运行时访问。
–add-opens打开包,意味着其中的所有类型和成员(不仅是公共类型)都可以在运行时访问。
主要区别在于–add-opens允许深度反射,即非公共成员的访问,才可以调用setAccessible(true)
参考: https://stackoverflow.com/questions/44056405/whats-the-difference-between-add-exports-and-add-opens-in-java-9

SGM需要加入:

--add-opens java.management/java.lang.management=ALL-UNNAMED 
--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED 
--add-opens java.management/sun.management=ALL-UNNAMED

R2M需要加入:

--add-opens java.base/java.time=ALL-UNNAMED

Ducc需要加入:

--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.util.concurrent.locks=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED 
--add-opens java.base/java.net=ALL-UNNAMED 
--add-opens java.base/sun.nio.ch=ALL-UNNAMED 

AKS需要加入:

--add-exports java.base/sun.security.action=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/sun.util.calendar=ALL-UNNAMED

🧩 启动后的验证

1.推荐先升级JDK11,再到JDK17,一边升级一边进行验证观察
2.观察日志是否有异常,特别是上面说到的启动时异常
3.观察监控类软件,可通过阿尔萨斯看具体指标
4.推荐逐步有序切量,并做好常态化压测,防止影响核心业务
5.升级完成后,最好能做个全流程的功能测试,防止功能异常

🏆 总结

1、升级后,除了可以使用新的语法特性,最大的亮点是可以使用亚毫秒级停顿的GC性能(至少百倍的GC性能提升),所以 强烈建议升级到JDK17
2、整个升级过程并不复杂,主要涉及到中间件版本的升级和启动参数的配置

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
### 回答1: Java JDK 11是Java开发工具包的一个版本,是由Oracle公司研发和发布的。JDKJava Development Kit)是一个用于开发和编译Java程序的软件包,它提供了Java语言开发环境和一系列的工具。JDK 11是Java的最新版本,于2018年9月发布。 JDK 11带来了许多新的特性和改进。其中一项重要的改变是,JDK 11中引入了模块化系统,即Java平台模块系统(JPMS)。该系统可以帮助开发者更好地管理和组织Java代码和依赖项。另一个重要的改进是JVM性能的提升,包括更快的启动时间和更低的内存占用。 除了这些改进,JDK 11还包含了一些新的API和功能,如HTTP Client API、新的垃圾收集器(ZGC)和低延迟的垃圾收集器(Shenandoah GC),以及对Unicode 10.0的支持。 CSDN是一个知名的IT技术社区和博客网站。它为开发者提供了大量的技术文章、博客、教程等资源。在CSDN上,你可以找到关于Java JDK 11的各种教程、问题解答、开发经验分享等内容。同时,CSDN还提供了很多其他与Java开发相关的资源,如Java框架、库、工具等的介绍和下载链接。 总结来说,Java JDK 11是Java开发工具包的一个版本,它带来了许多新的特性和改进。而CSDN是一个IT技术社区和博客网站,为开发者提供了很多与Java开发相关的资源。通过CSDN,你可以获取到关于Java JDK 11的各种教程和知识。 ### 回答2: Java JDK 11是Java开发工具包的最新版本JDKJava Development Kit的缩写,是开发Java应用程序所需的软件包。JDK 11具有许多新功能和改进,为Java开发者提供更好的工具和性能。 其中一个重要的新功能是模块系统的引入。模块系统允许开发者将代码划分为独立的模块,以提高代码的可重用性和维护性。这种模块化方法有助于构建更可靠和安全的应用程序。 此外,JDK 11还引入了新的语言特性和API。其中包括Lambda表达式的增强、局部变量类型推断和字符串API的改进。这些特性使得Java编程更加简洁和高效。 JDK 11还包含了Java Flight Recorder(JFR)的开源版本。JFR是一种强大的性能分析工具,可以实时监测和记录Java应用程序的性能指标。这使得开发者可以更好地识别和解决性能问题。 此外,JDK 11还具有增强的安全性和可扩展性。它支持TLS 1.3,这是一种更安全的网络协议。此外,JDK 11还提供了更好的内存管理和垃圾回收机制。 CSDN是中国最大的IT社区和技术交流平台。许多Java开发者在CSDN上分享他们的经验和知识。在CSDN上,你可以找到大量关于Java JDK 11的文章和教程,以帮助你学习和掌握JDK 11的新功能和用法。 总结来说,Java JDK 11是一个强大而且功能丰富的开发工具包,它提供了许多新功能和改进,使得Java开发更加简便和高效。CSDN是一个优秀的资源平台,可以帮助开发者更好地学习和应用JDK 11。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农佩奇

打酒喝!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值