1 JDK11升级JDK17最全实践干货来了-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JDK11升级JDK17最全实践干货来了

京东云 来源:jf_75140285 作者:jf_75140285 2024-06-25 14:50 次阅读

1、前言

如果你仍在使用JDK8,那你是否曾经遇到过OutOfMemoryError的问题?你是否曾经为JVM的调优问题感到困扰?本篇文章将为你介绍一种能够提供百倍性能提升的垃圾回收器,也许能够解决你的问题。

上篇文章给大家带来了JDK8升级JDK11的最全实践,相信大家阅读后已经对JDK11有了比较深入的了解。2021年9月14日,Oracle发布了可以长期支持的JDK17版本,那么从JDK11到JDK17,到底带来了哪些特性呢?亚毫秒级的ZGC效果到底怎么样呢?值得我们升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的JDK11升级JDK17最全实践。

2、为什么升级JDK17

1)长期支持版本

JDK17是Oracle官方在2021年9月14日发布的一个长期支持(LTS)版本,意味着它将获得长期的更新和支持,有助于保持程序的稳定性和可靠性。

2)性能提升

更好的垃圾回收器。综合评估,从Java 8 升级到 Java 11,G1GC平均速度提升16.1%,ParallelGC为4.5%,从Java 11 升级到 Java 17,G1GC平均速度提升8.66%,ParallelGC为6.54%(基于OptaPlanner的用例基准测试表明)

最大的亮点是带来了稳定版的ZGC垃圾回收器,达到亚毫秒级停顿。

3)新语法和特性

Switch表达式简化、Text Blocks文本块、instanceof 的模式匹配升级和NullPointerException提示信息改进等

4)支持最新的技术和框架

Spring framework6 和Spring Boot3 都默认使用 Java 17作为最低版本



3、升级后压测效果

先给出结论: 1、JDK17相对于JDK8和JDK11,所有垃圾回收器的性能都有很明显的提升,特别是稳定版的ZGC垃圾回收器 2、不论任何机器配置下,都推荐使用ZGC,ZGC的停顿时间达到亚毫秒级,吞吐量也比较高

我在JDOS平台上选择了不同配置的机器(2C4G、4C8G、8C16G),并分别使用JDK8、JDK11和JDK17进行部署和压测。

整个压测过程限时60分钟,用180个虚拟用户并发请求一个接口,每次接口请求都创建512Kb的数据。最终产出不同GC回收器的各项指标数据,来分析GC的性能提升效果。

以下是压测的性能情况:

wKgZomZ6aKOAD22HAAi5bgySCzk303.png

4、OracleJDK 和 OpenJDK 的选择

2021年9月,Oracle宣布JDK17可以免费商用,直到下一个 LTS 版本之后继续提供整整一年,同时Oracle 将继续按照自 Java 9 以来的相同版本和时间表提供GPL下的Oracle OpenJDK 版本。

2023年9月,OracleJDK发布了新的LTS版本 JDK21,这就意味着从2024年9月开始,在生产环境使用 OracleJDK17 将需要付费。

wKgaomZ6aKSAGisKAAMRRt5X6Rg181.png

参考: https://www.oracle.com/hk/java/technologies/downloads/#java17



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

wKgZomZ6aKWAc-FsAAew_nLwmbk872.png

官方参考: https://blogs.oracle.com/java/post/oracle-jdk-releases-for-java-11-and-later



5、JDK11到JDK17带来了哪些新特性

5.1、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,可以清晰描述具体变量,提升了空指针异常的可读性。

wKgaomZ6aKWAKewWAAKvKS-aymI069.png

5.2、新语法特性

5.2.1、Switch表达式简化

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

参考: https://openjdk.org/jeps/361

传统写法

wKgZomZ6aKaAdHaPAAKYFRliiLI202.png

新写法

wKgaomZ6aKeAJyT6AAKGfMXr05s115.png

5.2.2、Text Blocks文本块(5星推荐)

参考: https://openjdk.org/jeps/378

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

wKgZomZ6aKmAGYN9AAJbdcNO_qo178.png

5.2.3、Record类型

参考: https://openjdk.org/jeps/395

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

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

wKgaomZ6aKqAI38CAADCFJb_fUE317.png

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

wKgZomZ6aK2APK0NAAOzoF7hTRM853.png

wKgaomZ6aLCAIGMiAAYDSCdPJgk363.png

3、使用Point类

wKgZomZ6aLGAISGRAASxTpqWAFg021.png

5.2.4、instanceof 的模式匹配升级

•instanceof类型判断再也不需要强制转换

参考: https://openjdk.org/jeps/394

wKgaomZ6aLKAZm2qAAFvXPHvNW8875.png

5.2.5、密封的类和接口

参考: https://openjdk.org/jeps/409

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

使用修饰符sealed,您可以将一个类声明为密封类。密封的类使用关键字permits列出可以直接扩展它的类。子类可以是最终的,非密封的或密封的

比较实用的一个特性,可以用来限制类的层次结构

wKgaomZ6aLSAOd3EAAYcu0hyX9M222.png

5.2.6、其他优化和升级

感兴趣的同学,推荐阅读OpenJDK官方文档说明,从JDK11到JDK17的改动: https://openjdk.org/projects/jdk/17/jeps-since-jdk-11



6、升级步骤

6.1、JDK选择

OpenJDK17下载:https://jdk.java.net/archive/

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

6.2、pom编译配置升级

maven编译所需JDK升级至17

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

6.3、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升级指南: https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions

springboot版本官网: https://spring.io/projects/spring-boot#learn

循环依赖问题

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

wKgZomZ6aLSARSB1AABxcsskNw0288.png

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

解决方案:

第一种:推荐更新应用中bean的依赖关系来解决

第二种:配置文件中加入以下配置,为了和旧版本保持一致,此配置推荐添加

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

6.4、常用中间件升级

6.4.1、Lombok版本升级到1.18.20以上

< dependency >
 < groupId >org.projectlombok< /groupId >
 < artifactId >lombok< /artifactId >
 < version >1.18.20< /version >
< /dependency >

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

wKgaomZ6aLWAWaEkAACi1Vztzrk001.png



6.4.2、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

解决方案:

properties配置(感谢 zhaoyongping8 验证提供):

spring.mvc.pathmatch.matching-strategy=ant_path_matcher

/**
 * 增加如下配置可解决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);
            }
        }
    };
}

参考:https://developer.aliyun.com/article/950787

6.4.3、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 >

6.4.4、Concrete配置中心阻塞升级

新版本 2.2.1 已修复此问题

使用 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行)的实现逻辑

wKgZomZ6aLeATpYaAAny-8-la8g598.png

wKgaomZ6aLqAHMaSAAoGmonrCSM034.png

解决方案:

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()) 本来就要跳过静态字段,这样修改直接避免了访问。推动concrete团队修复问题



6.5、JVM启动参数配置

6.5.1、开启ZGC

启动参数中配置:-XX:+UseZGC

wKgZomZ6aL6AdkgoAAIGFO8NFzk334.png

6.5.2、不同中间件所需启动参数

升级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

6.6、启动后的验证

1.推荐先升级JDK11,再到JDK17,一边升级一边进行验证观察

2.观察日志是否有异常,特别是上面说到的启动时异常

3.观察监控类软件,比如SGM、UMP等监控是否正常

4.推荐逐步有序切量,并做好常态化压测,防止影响核心业务

5.升级完成后,最好能做个全流程的功能测试,防止功能异常



7、总结

1、升级后,除了可以使用新的语法特性,最大的亮点是可以使用亚毫秒级停顿的GC性能(至少百倍的GC性能提升),所以 强烈建议升级到JDK17

2、整个升级过程并不复杂,主要涉及到中间件版本的升级和启动参数的配置

如果还停留在JDK8,推荐先升级JDK11,再到JDK17,具体升级步骤先参考我的上篇文章“JDK8升级JDK11最全实践干货来了”,再参考本章中的升级步骤。

希望以上分享可以给大家带来实际的帮助,升级过程中如果遇到问题,欢迎大家在评论区回复。



系列文章:

JDK8升级JDK11最全实践干货来了

JDK11升级JDK17最全实践干货来了

你还在“垃圾”调优?快来看看JDK17的ZGC如何解放双手

JDK17实践升级经验汇总

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编译
    +关注

    关注

    0

    文章

    657

    浏览量

    32851
  • JDK
    JDK
    +关注

    关注

    0

    文章

    81

    浏览量

    16592
收藏 人收藏

    评论

    相关推荐

    JDK的安装、环境配置及使用

    JDK的安装、环境配置及使用 JDK的安装与环境配置Java是由Sun公司开发的,从Sun公司的Internet站点可以找到最新版本JDK。以Windows系统为例,JDK的安装与环境
    发表于 12-06 00:19

    树莓派安装JDK

    树莓派安装JDK更新:2017-03-051、在sun官网下载jdk,我们下载jdk7版本的http://www.oracle.com/technetwork/java/javase
    发表于 03-05 15:12

    JDK11主要工具

    JDK11的工具的命令参考
    发表于 08-13 08:21

    Java SecurityArchitecture (JDK

    implemented for JDK 1.2, introduces the new classesand their usage, discusses the impact of this new architecture on existing code,a
    发表于 10-14 17:38 10次下载

    java jdk6.0官方下载

    java jdk6.0下载如何件: java jdk6.0安装步骤: 第一步 JDK1.6的安装步骤 第一步双击安装文件jdk-6u7-windows-i586-p.exe
    发表于 10-17 11:47 155次下载
    java <b class='flag-5'>jdk</b>6.0官方下载

    如何解决JDK8小版本升级后性能下降的问题

    编者按:在升级 JDK8U 的小版本后(从 8u74 升级到 8u202),遇到性能剧烈下降的问题(性能下降 13 倍)。该应用是一个非常简单的 Web 应用,且应用在 JDK
    的头像 发表于 07-26 14:44 4098次阅读
    如何解决<b class='flag-5'>JDK</b>8小版本<b class='flag-5'>升级</b>后性能下降的问题

    毕昇JDK8和JDK11首次同时发布两个版本

    2021 年 9 月 30 日,毕昇 JDK update Q3 版本正式发布,本次发布将包含 X86_64 版本。此前,毕昇 JDK 只发布 Aarch64 版本,这可能会对运维产生一定
    的头像 发表于 10-28 10:53 3291次阅读
    毕昇<b class='flag-5'>JDK</b>8和<b class='flag-5'>JDK11</b>首次同时发布两个版本

    基于JDK 1.8来分析Thread类的源码

    由上图我们可以看出,Thread类实现了Runnable接口,而Runnable在JDK 1.8中被@FunctionalInterface注解标记为函数式接口,Runnable接口在JDK 1.8中的源代码如下所示。
    的头像 发表于 02-06 17:12 622次阅读

    JDK11升级JDK17最全实践

    2021年9月14日,Oracle发布了可以长期支持的JDK17版本,那么从JDK11JDK17,到底带来了哪些特性呢?亚毫秒的ZGC效
    的头像 发表于 11-17 10:36 1505次阅读
    <b class='flag-5'>JDK11</b><b class='flag-5'>升级</b><b class='flag-5'>JDK17</b><b class='flag-5'>最全</b><b class='flag-5'>实践</b>

    weblogic修改jdk路径

    WebLogic是一个流行的Java应用服务器,可以用于部署和管理企业Java应用程序。在WebLogic的安装和配置过程中,我们可能会遇到需要修改JDK(Java Development Kit
    的头像 发表于 12-05 14:46 1298次阅读

    eclipse配置servers识别不到jdk

    Eclipse是一个广泛使用的集成开发环境(IDE),它可以帮助开发人员编写、调试和测试许多编程语言。在使用Eclipse时,可能会遇到配置servers时无法识别JDK的问题,这会导致项目无法正常
    的头像 发表于 12-06 11:41 883次阅读

    如何在eclipse配置jdk环境

    在Eclipse中配置JDK环境非常重要,它是开发Java程序的基础。本文将详细介绍如何在Eclipse中配置JDK环境。 下载和安装JDK 首先,在Oracle官网上下载适用于您的操作系统的
    的头像 发表于 12-06 11:49 1647次阅读

    idea的jdk配置在哪

    在开发Java应用程序时,我们通常需要配置Java开发工具包(JDK),以便能够在我们的集成开发环境(IDE)中编写和运行Java代码。本文将详细介绍如何在Idea中配置JDK,并提供相关细节的说明
    的头像 发表于 12-06 15:04 2586次阅读

    如何配置jdk的环境变量

    /javase-jdk11-downloads.html)选择适合您操作系统的JDK版本,并下载它。 第二步:安装JDK 下载完成后,运行JDK的安装程序,并按照提示进行安装。选择一个
    的头像 发表于 12-06 15:07 833次阅读

    JDK8升级JDK11最全实践干货来了

    呢?值得我们升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的JDK8升级JDK11
    的头像 发表于 06-25 14:51 428次阅读
    <b class='flag-5'>JDK</b>8<b class='flag-5'>升级</b><b class='flag-5'>JDK11</b><b class='flag-5'>最全</b><b class='flag-5'>实践</b><b class='flag-5'>干货</b><b class='flag-5'>来了</b>