首页 > 其他分享 >【转】JDK8 升级 JDK11 最全实践干货来了 | 京东云技术团队

【转】JDK8 升级 JDK11 最全实践干货来了 | 京东云技术团队

时间:2023-11-15 13:44:07浏览次数:52  
标签:Java 模块化 module 干货 升级 JDK8 JDK11 模块

原文地址:JDK8 升级 JDK11 最全实践干货来了 | 京东云技术团队
作者:京东云开发者

1.前言

截至目前(2023 年),Java8 发布至今已有 9 年,2018 年 9 月 25 日,Oracle 发布了 Java11,这是 Java8 之后的首个 LTS 版本。那么从 JDK8 到 JDK11,到底带来了哪些特性呢?值得我们升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的 JDK8 升级 JDK11 最全实践。

2.为什么升级 JDK11

1)性能提升

更好的垃圾收机制、更快的类加载器,加快应用程序的运行速度。综合评估,从 Java 8 升级到 Java 11,G1GC 平均速度提升 16.1%,ParallelGC 为 4.5%(基于 OptaPlanner 的用例基准测试表明)

2)特性和改进

局部变类型推断、新的 API、HTTP/2 客户端、Lambda 表达式的新特性等,这些新特性可以提高开发效率。

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

许多新的技术和框架已经或即将开始依赖于 JDK11 或以上版本,升级后可以保证应用程序能够分利用这些新的技术和框架。

4)长期支持版本

JDK11 是 Oracle 官方发布的一个长期支持(LTS),意味着它将获得长期的更新和支持,有助于保持用程序的稳定性和可靠性。

5)行业趋势

数据来自 New Relic 在 2023 年 1 月发布的 Java 生态报告,从下图可以看出:

目前市面上有 超过 56% 的应用程序使用了 JDK 11,Java 8 的使用从 2020 年的 84% 降低到了现在的 32% 左右。大部分公司在这三年之间都升级到了 JDK 11 或者 JDK 17 这两个 LTS 版本上面。
垃圾收集器使用情况来看,JDK11 版本及以上 G1 使用率最高,占比高达 65%
image

3.升级后 GC 效果

先给出结论:

JDK11 相对于 JDK8,所有垃圾回收器的性能都有提升,特别是大内存机器下 G1 的提升最明显
8G 内存以下的机器,推荐使用 Parallel GC,如果特别追求低延迟,选择牺牲吞吐量,可以使用 G1,并设置期望的最大垃圾回收停顿时间来控制
8G 及以上的大内存机器,推荐使用 G1 4、不推荐使用 CMS,升级后从各项数据来看,CMS 收集器都不如 G1
我在 JDOS 平台上选择了不同配置的机器(2C4G、4C8G、8C16G),并分别使用 JDK8 和 JDK11 进行部署和压测。

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

以下是压测的性能情况:
image

  • 上面给出的 GC 升级效果,采用的是默认的配置,没有做任何优化,只提供参考。真正的 GC 调优是个技术活,需要根据业务需求、机器配置和实际压测效果等综合评估来选出最合适的 GC 垃圾回收器。

  • 不同垃圾回收器的特点:

  1. Parallel GC - JDK 8 及以下版本的默认收集器,关注吞吐量,尝试在最小延迟的情况下尽快完成工作并提高吞吐量。

  2. CMS - 一个老年代收集器,基于标记 - 清除算法实现,关注延迟,以最短回收停顿时间为目标

  3. Garbage First(G1)- JDK 9 以后的默认收集器,G1 关注总体的性能,会尝试在吞吐量和延迟之间做平衡。

4.JDK11 带来了哪些新特性

1.GC 改进

默认垃圾回收器改为 G1,废弃 CMS 垃圾回收器

◦ G1 特点:目标是降低应用程序的停顿时间并提高吞吐量。

引入 ZGC 垃圾回收器(可伸缩低延迟垃圾收集器) 但由于 JDK11 中 ZGC 还不够完善,推荐在 JDK17 中再使用稳定版 ZGC

◦ Full GC 的停顿不超过 10 毫秒

◦ 支持 TB 级堆内存回收

◦ 相对于 G1 吞吐量下降不超过 15%

2.模块化

Java9 引入了对于模块化软件支持,而 Java11 进一步扩展了这种特性。模块化让应用程序 更精简,减少对其他类库的依赖和冗余代码,提高运行效率和安全性 。

然而,目前不推荐使用模块化,因为相关组件生态还不完善,并且模块化带来的价值不够突出。具体原因请看后面章节的详细分析:新特性实践 - 模块化。

3.语法增强

◦ 局部变量推断,引入 var 局部变量类型,允许开发人员省略通常不必要的局部变量类型初始化声明
image
◦ Lambda 表达式简化,内部可以使用 var
image
◦ 接口中可以定义私有方法,可以实现接口方法的访问控制和代码复用
image

4.API 增强

◦ HTTPClient 标准化支持:强大而灵活的 HTTP 客户端 API,支持多协议(HTTP/2、WebSocket)、异步非阻塞、流操作和连接池等特性。ps:再也不需要用第三包 HttpClient 工具包

◦ 字符串方法增强:isBlank、lines、strip、stripLeading、stripTrailing 和 repeat

◦ Files 增强:readString、WriteString

◦ InputStream 增强:transferTo(流快速拷贝)

◦ stream 增强,dropWhile(从集合中删除满足的)、takeWhile(从集合中获取满足的)、ofNullable

◦ 集合工厂方法:Sets.of ()、List.of ()、Map.of ()、Map.ofEntries (),举例:List list = List.of ("Java", "Python", "C++");

5. 如何升级

1.升级应用评估

为保证稳定性,我们优先在新业务新应用来落地实施 JDK11 的升级。

2.JDK 选择

自从 2019 年 1 月起,Oracle JDK 后续的版本开始商用收费,所以推荐大家选择 OpenJDK11,OpenJDK 和 OracleJDK 功能上没有差异,支持免费商用。

OpenJDK11 下载地址:https://jdk.java.net/archive/

3.GC 配置

根据自身需求和机器配置选择 GC,不同 GC 的 JVM 启动参数配置:

G1 垃圾回收器(JDK11 默认,不需要手动配置):-XX:+UseG1GC
Parallel GC 垃圾回收器:XX:+UseParallelGC

4.升级过程踩坑

整个升级过程还是比较简单的,除了升级 JDK 版本,实际遇到的问题如下:
image

5.升级后验证

升级后完成,做好单测和回归测试,推荐能做个压测验证,防止影响线上服务稳定性

6.新特性实践 - 模块化

Java 一直是构建大型应用程序的主流语言之一。然而随着 Java 生态系统中存在着大量库和复杂的代码块之间关系难以理清的问题,构建系统变得困难且超出了我们的理解和有效开发的范围。特别是在使用繁多的 Java 存档文件(Java Archive, JAR)时,这一问题变得更加突出。为了应对这种复杂性,模块化能够很好地管理和减少代码的复杂性。因此自 Java9 开始,引入了模块化系统。通过模块化,Java 本身也得以进行模块化改进。

1.模块化是什么?

模块化指的是 JAVA 平台的模块系统(Java Platform Module System),简称 JPMS。JPMS 引入一种新方式来组织和构建 Java 应用程序,它将代码分为相互独立、可复用的模块。每个块都有自己的命名空间,明确声明并控制其他模块的访问权限。这种模块化设计使得开发人员能够更好地维护复杂的应用程序,提高代码的复用性、可维护性和安全性,同时提升应用的加载速度和性能 最大的特点是可以定义模块描述符来隔离 module(Jar 包)内部类的访问权限。

模块化的几点关键说明:

1)相对于 JDK8 的变动

JDK9 以后引入了一个新组件 module:模块描述符 module-info.java,用于将一组相关的包放入一个组中。
在 Java8 和更早的应用程序中,应用程序将包作为顶级组件,Java9 以后应用程序将模块作为顶级组件
一个模块(Jar 包)只能有一个 module-info.java。
2)和 maven 的关系

模块化并不是要替代 maven,和 maven 本身并不冲突,maven 定义 jar 之间的依赖关系,模块化是对已经依赖的 jar 下的包进行更细粒度依赖控制

3)如何兼容旧应用

天然兼容旧应用。为了向后兼容旧项目,一些库本身并未模块化,其仍然可以作为模块在模块路径中使用,而这些库在模块路径上时会被转化为自动模块,例如:jackson-databind-1.0.0.jar 将成为自动模块 jackson.databind

image

2.带来了哪些好处?

1)封装和隔离,更好的访问控制

模块化允许开发者将代码和资源封装在独立的模块中。模块之间可以明确地定义公开和私有的 API,提供了更好的代码隔离性和可维护性。

ps:新业务单应用可以按照领域模型来进行多模块的划分,以避免代码腐化。简单举例单应用下存在产品.jar、订单.jar。订单依赖产品,通过模块化的限制,订单只能使用产品中明确对外暴露的类,这样就避免传统模式订单.jar 可能依赖了产品.jar 中普通的类导致代码腐化的问题,也降低后续领域服务拆分的复杂度

2)更好的可伸缩性,加载速度的提升

模块化系统使得 Java 平台更加可伸缩,通过模块化定义,可以仅加载需要的模块,从而提升加载类的效率,最终减少了应用程序的内存占用和启动时间,同时打包后的程序也更小。

3)明确的依赖关系

模块化系统要求在模块之间明确定义依赖关系。在编译或运行代码之前,模块系统会检查模块是否满足所有依赖关系,从而导致更少的运行时错误。

4)安全

在 JVM 的最深层次上执行强封装,减少 Java 运行时的攻击面,同时无法获得对敏感内部类的反射访问。

3.如何使用

1)定义 module-a.jar

包结构如下:

com.jdt.a
        person
            Men.java
        reflect
            ReflectModel.java
        module-info.java    

module-info 文件内容如下:

module module.a { 
    //指令用于指定一个模块中哪些包下的public对外是可访问的,包括直接引入和反射使用 
    exports com.jdt.a.person; 
    // 只能被反射调用,用于指定某个包下所有的 public 类都只能在运行时可被别的模块进行反射,并且该包下的所有的类及其乘员都可以通过反射进行访问。 
    opens com.jdt.a.refect; 
}

2)定义 module-b.jar, 包的 pom 中指定依赖了 module-a

包结构如下:

com.jdt.b
        test
            Test.java
        module-info.java    

module-info 文件内容如下:

module module.b {
     //依赖a下的包
     requires module.a;
}

3)此时 module-b.jar, 在编写编码时,会遇到如下问题
image

4.实践过程的坑

上面简单介绍了模块化的知识,具体在落地过程中,我们主要踩了以下的坑,供大家参考

1)依赖 JSF 包时无法模块化

  • JSF 是京东内部使用的高性能 RPC 框架

进行模块化时,pom 中依赖了 jsf 包,模块定义如下:

module module.a {
    requires  fastjson;
    //依赖jsf包名
    requires  jsf.lite;

    exports com.jd.jdk.test.module;
}


此时编译报错如下:提示找不到模块:jsf.lite,但是 pom 中明明指定依赖了 jsf.lite
image
问题原因:

经过一系列定位研究,发现 jsf-lite 包中,/META-INF/services 下的文件 org.glassfish.jersey.internal.spi.AutoDiscoverable 里面写的类是 com.alibaba.fastjson.support.jaxrs.FastJsonAutoDiscoverable,此类并未在当前 jsf.lite 包中定义,属于 com.alibaba.fastjson 包的。
image
但是我们的 pom 中明明也依赖了 com.alibaba.fastjson 包,为什么模块化后,就找不到了呢?

主要原因在于模块化遇到 SPI(Service Provider Interface)时的约束:模块化时,SPI 机制要求配置中定义依赖的类必须本模块定义的,不能是其他模块的包(来自它不拥有的包),否则,此包将无法被模块化

这样也就解释了,为什么上面 jsf 无法找到 module 的问题,jsf-lite 里面设置了它不拥有的包:com.alibaba.fastjson.support.jaxrs.FastJsonAutoDiscoverable,导致 jsf-lite 包无法被自动模块化

解决方案:

1、联系 JSF 团队,升级 JSF 包,修复上面说的 FastJsonAutoDiscoverable 配置错误的问题。

2)拆包问题(模块隔离)

模块化约束:jdk9 以上,使用模块化时不支持拆分包的形式依赖

拆分包意味着两个模块包含相同的包,Java 模块系统不允许拆分包。拆分包始终是不正常的,而当使用解析可传递依赖项的构建工具(如 Maven 等)时,很容易出现同一个库的多个版本,当 Java 模块系统检测到一个包存在于模块路径上的多个模块中时,就会拒绝启动。

例如:

module-a.jar包结构定义:
com.foo.package
    A.java
    
module-b.jar包结构定义:
com.foo.package
    B.java   

当 module-c 同时依赖 module-a 和 module-b 时,如上编译时会报一个错,Package com.foo.package in both module module.b and module module.a,这就是 JAVA9 的模块隔离,要求只能从一个模块(module)中读取同一个包(package),不能跨模块读取。

解决方案:

如果在使用模块化时,遇到了拆分包问题,无论如何都是无法绕过的。即使从用户角度来看基于类路径的应用程序可以正确工作,你也最终需要处理这些问题。此时只能停用模块化或升级 jar 包,避免拆分包问题

5.模块化落地总结

目前不推荐使用模块化,因为相关组件生态还不完善,并且模块化带来的价值不够突出:

很多中间件都是基于 jdk8 构建的,都有可能遇到模块化兼容的问题,比如:jsf,需要 jsf 强制升级才可以使用模块化

拆包问题无法解决,比如:aws-java-sdk-s3、fluent 等。

7.总结

升级过程简单,升级后可以使用更多新特性和更好的 GC 性能,所以 建议升级到 JDK11。
现阶段 不推荐使用模块化,但是不用担心会影响 JDK11 的升级。
另外听说 JDK17 的 ZGC 可以达到亚秒级停顿,但考虑到 JDK11 的 ZGC 还不是很稳定,所以本次不做测试,后面升级到 JDK17 后再给大家分享 ZGC 压测效果。

希望以上分享可以给大家带来实际的帮助。

作者:京东科技 曲振富

来源:京东云开发者社区 转载请注明来源

标签:Java,模块化,module,干货,升级,JDK8,JDK11,模块
From: https://www.cnblogs.com/fanqisoft/p/17833644.html

相关文章

  • 【转】JDK11 升级 JDK17 最全实践干货来了 | 京东云技术团队
    原文地址:JDK11升级JDK17最全实践干货来了|京东云技术团队原文作者:京东云开发者1.前言上篇文章给大家带来了JDK8升级JDK11的最全实践,相信大家阅读后已经对JDK11有了比较深入的了解。2021年9月14日,Oracle发布了可以长期支持的JDK17版本,那么从JDK11到JDK17,......
  • # yyds干货盘点 # 由['a', 'b', 'c']变为['c', 'a', 'b&
    大家好,我是皮皮。一、前言前几天在Python最强王者交流群【吴超建】问了一个Python基础问题,一起来看看吧。由['a','b','c']变为['c','a','b','c']请教下有没有优雅一点的写法?二、实现过程这里【巭孬......
  • Linux-AT命令干货分享,还不赶紧收藏!
    AT简介AT即Attention,AT指令集是从终端设备(TerminalEquipment,TE)或数据中断设备(DataTerminalEquipment,DTE)向终端适配器(TerminalAdapter,TA)或数据电路终端设备(DataCircuitTerminalEquipment,DCE)发送的。通过TA,TE发送AT指令来控制移动台(MobileStation,MS)的功能,与GSM......
  • # yyds干货盘点 # 从5亿行数据中,筛选出重复次数在1000行的数据行,也爆内存了
    大家好,我是皮皮。一、前言前几天在Python最强王者交流群【巭孬......
  • JDK11->JDK17问题记录一(又jenkins使用问题记录一)
    背景:springboot项目jdk版本从11升级至17,本地打包编译OK,将代码提交至gerrit仓库时触发编译报错,错误如下:09:29:02[ERROR]Failedtoexecutegoalorg.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile(default-compile)onprojectXXX:Fatalerrorcompiling:inva......
  • #yyds干货盘点#react之useEffect
    React的HooksAPI为我们提供了一种新的处理副作用的方式——useEffect。useEffect函数接受两个参数:一个是_副作用函数_和一个_依赖数组_。副作用函数是在组件render之后运行,而依赖数组告诉React何时应该执行或跳过该副作用。如果没有提供依赖数组,`useEffect`将在每次渲染后运行。......
  • # yyds干货盘点 # 对5亿行数据去重,各位有没有啥方法。。。内存直接爆了
    大家好,我是皮皮。一、前言前几天在Python最强王者交流群【巭孬......
  • 性能测试复习准备——linux环境下安装jdk8
     先在根目录下创建目录: /soft/jdk8  ——用于放上传进来的软件包;       然后通过左边的上传按钮,把包上传到 /soft/jdk8下面; 然后在根目录下面创建/evir/jdk8目录,用户放解压后的软件包;tar-zxvffile.tar.gz-C/path/to/destination   tar......
  • #yyds干货盘点#react的useState源码分析
    简单说下为什么React选择函数式组件,主要是class组件比较冗余、生命周期函数写法不友好,骚写法多,functional组件更符合React编程思想等等等。更具体的可以拜读dan大神的blog。其中Functioncomponentscapturetherenderedvalues这句十分精辟的道出函数式组件的优势。但是在16.8之......
  • 【虹科干货】TWAMP:什么是双向主动测量协议?
    TWAMP(双向主动测量协议)是什么?它在网络性能测量中有什么作用?如果您对IP网络中设备之间的性能问题感兴趣,或者想了解TWAMP与OWAMP之间的区别,以及TWAMP测试的好处。 一、TWAMP是什么?TWAMP代表“双向主动测量协议”,如RFC5357中所述,它是一种用于测量IP网络中任意两个设备或端点之间的网......