JDK8升级JDK11最全实践干货来了
Tech导读
截至目前(2023年),Java8发布至今已有9年,2018年9月25日,Oracle发布了Java11,这是Java8之后的首个LTS版本。那么从JDK8到JDK11,到底带来了哪些特性呢?值得升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的JDK8升级JDK11最全实践。
导读
截至目前(2023年),Java8发布至今已有9年,2018年9月25日,Oracle发布了Java11,这是Java8之后的首个LTS版本。那么从JDK8到JDK11,到底带来了哪些特性呢?值得升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的JDK8升级JDK11最全实践。01 为什么升级JDK11
在今年的敏捷团队建设中,我通过Suite执行器实现了一键自动化单元测试。Juint除了Suite执行器还有哪些执行器呢?由此我的Runner探索之旅开始了!
1.1 性能提升
更好的垃圾收机制、更快的类加载器,加快应用程序的运行速度。综合评估,从Java 8 升级到 Java 11,G1GC平均速度提升16.1%,ParallelGC为4.5%(基于OptaPlanner的用例基准测试表明https://www.optaplanner.org/blog/2019/01/17/HowMuchFasterIsJava11.html)。
1.2 特性和改进
局部变类型推断、新的 API、HTTP/2客户端、Lambda表达式的新特性等,这些新特性可以提高开发效率。
1.3 支持最新的技术和框架
许多新的技术和框架已经或即将开始依赖于JDK11或以上版本,升级后可以保证应用程序能够分利用这些新的技术和框架。
1.4 长期支持版本
JDK11是Oracle官方发布的一个长期支持(LTS),意味着它将获得长期的更新和支持,有助于保持用程序的稳定性和可靠性。
1.5 行业趋势
数据来自 New Relic (https://newrelic.com/resources/report/2023-state-of-the-java-ecosystem)在2023年1月发布的Java生态报告,从下图可以看出:
目前市面上有超过 56%的应用程序使用了JDK 11,Java 8 的使用从2020年的84%降低到了现在的32%左右。大部分公司在这三年之间都升级到了JDK 11 或者 JDK 17这两个LTS版本上面。
垃圾收集器使用情况来看,JDK11版本及以上G1使用率最高,占比高达65%。
02
升级后GC效果
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
2、8G内存以下的机器,推荐使用Parallel GC,如果特别追求低延迟,选择牺牲吞吐量,可以使用G1,并设置期望的最大垃圾回收停顿时间来控制;
4、不推荐使用CMS,升级后从各项数据来看,CMS收集器都不如G1。
在JDOS平台上选择了不同配置的机器(2C4G、4C8G、8C16G),并分别使用JDK8和JDK11进行部署和压测。
整个压测过程限时60分钟,用180个虚拟用户并发请求一个接口,每次接口请求都创建512Kb的数据。最终产出不同GC回收器的各项指标数据,来分析GC的性能提升效果。
机器配置 | 垃圾回收器 | 指标项 | JDK8 | JDK11 | JDK11比JDK8提升 | 总结 |
2C4G | Parallel GC(标记复制+标记整理) | 吞吐量 | 88.805% | 92.821% | 4% | 1、JDK11各项指标都有提升 2、当前机器配置下,综合评估,Parallel GC的综合指标比G1高 |
平均停顿GC时间 | 28.3ms | 19.6ms | 30% | |||
最大停顿GC时间 | 720ms | 720ms | 0 | |||
CMS(标记复制+标记清除) | 吞吐量 | 58.551% | 63.923% | 5% | ||
平均停顿GC时间 | 28.0ms | 26.5ms | 7% | |||
最大停顿GC时间 | 300ms | 250ms | 16% | |||
G1收集器 | 吞吐量 | 83.046% | 68.371% | -15% | ||
平均停顿GC时间 | 125ms | 49.9ms | 60% | |||
最大停顿GC时间 | 1170ms | 610ms | 47% | |||
4C8G | Parallel GC(标记复制+标记整理) | 吞吐量 | 90.851% | 95.252% | 5% | 1、JDK11各项指标都有明显提升 2、当前机器配置下,综合评估,G1的综合指标比Parallel GC高 |
平均停顿GC时间 | 27.1ms | 15.3ms | 43% | |||
最大停顿GC时间 | 580ms | 680ms | -17% | |||
CMS(标记复制+标记清除) | 吞吐量 | 49.812% | 56.55% | 7% | ||
平均停顿GC时间 | 38.3ms | 32.3ms | 15% | |||
最大停顿GC时间 | 180ms | 150ms | 16% | |||
G1收集器 | 吞吐量 | 96.333% | 97.328% | 1% | ||
平均停顿GC时间 | 18.4ms | 18.7ms | 0.01% | |||
最大停顿GC时间 | 980ms | 190ms | 80% | |||
8C16G | Parallel GC(标记复制+标记整理) | 吞吐量 | 90.114% | 94.718% | 4% | 1、JDK11各项指标都有明显提升 2、当前机器配置,综合评估,大内存机器,G1的综合指标比Parallel GC高很多 |
平均停顿GC时间 | 30.8ms | 16.8ms | 46% | |||
最大停顿GC时间 | 940ms | 770ms | 18% | |||
CMS(标记复制+标记清除) | 吞吐量 | 53.893% | 60.168% | 7% | ||
平均停顿GC时间 | 32.2ms | 27.2ms | 15% | |||
最大停顿GC时间 | 260ms | 100ms | 61% | |||
G1收集器 | 吞吐量 | 96.359% | 97.143% | 1% | ||
平均停顿GC时间 | 20.1ms | 17.3ms | 14% | |||
最大停顿GC时间 | 260ms | 120ms | 53% |
* 不同垃圾回收器的特点:
1.Parallel GC - JDK 8及以下版本的默认收集器,关注吞吐量,尝试在最小延迟的情况下尽快完成工作并提高吞吐量。
2.CMS - 一个老年代收集器,基于标记-清除算法实现,关注延迟,以最短回收停顿时间为目标。
03
JDK11带来了哪些新特性
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目标页面展示到屏幕。
3.1 GC改进
G1特点:目标是降低应用程序的停顿时间并提高吞吐量。
引入ZGC垃圾回收器(可伸缩低延迟垃圾收集器);但由于JDK11中ZGC还不够完善,推荐在JDK17中再使用稳定版ZGC
Full GC的停顿不超过10毫秒 支持TB级堆内存回收 相对于G1吞吐量下降不超过15%
3.2 模块化
Java9引入了对于模块化软件支持,而Java11进一步扩展了这种特性。模块化让应用程序更精简,减少对其他类库的依赖和冗余代码,提高运行效率和安全性。
3.3 语法增强
局部变量推断,引入var局部变量类型,允许开发人员省略通常不必要的局部变量类型初始化声明
Lambda表达式简化,内部可以使用var
接口中可以定义私有方法,可以实现接口方法的访问控制和代码复用
3.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<String> list = List.of("Java", "Python", "C++")
04 如何升级
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
4.1 升级应用评估
为保证稳定性,优先在新业务新应用来落地实施JDK11的升级。
4.2 JDK选择
4.3 GC配置
G1垃圾回收器(JDK11默认,不需要手动配置):-XX:+UseG1GC Parallel GC垃圾回收器:XX:+UseParallelGC
4.4 升级过程踩坑
分类 | 依赖名 | 支持情况 | 说明 |
框架 | Spring2.X/boot | 支持 | 使用JDK11自带原生HttpClient时,会遇到:1、spring启动时,会遇到注入某些类时,无法通过反射的方式访问其所在的包,报错:module java.net.http does not"opens jdk.internal.net.http"to unnamed module @5eb2172 原因:模块化引入了包之间的访问权限控制,如果没有对一个包显示地使用open/opens关键字对外开放,那么其他包中的类无法通过反射的方式访问此包。 解决方案:需要手动设置JVM参数,比如:--add-opens java.net.http/jdk.internal.net.http=ALL-UNNAMED |
中间件 | JSF | 支持 | |
AKS | 支持 | 出现异常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> | |
Mybatis | 支持 | ||
Concrete | 支持 | ||
R2M | 支持 | ||
EasyJob | 支持 | ||
OSS | 支持 | ||
FMQ | 支持 | ||
监控运维 | SGM | 支持 | |
UMP | 支持 | ||
UWC | 支持 | ||
CICD | JDOS部署 | 支持 | JDK11镜像:java-jdt-centos7.4-jdk1.11.0_13-tomcat9.0.54:latest |
4.5 升级后验证
升级后完成,做好单测和回归测试,推荐能做个压测验证,防止影响线上服务稳定性。
05 新特性实践-模块化
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
Java一直是构建大型应用程序的主流语言之一。然而随着Java生态系统中存在着大量库和复杂的代码块之间关系难以理清的问题,构建系统变得困难且超出了大家的理解和有效开发的范围。特别是在使用繁多的Java存档文件(Java Archive, JAR)时,这一问题变得更加突出。为了应对这种复杂性,模块化能够很好地管理和减少代码的复杂性。因此自Java9开始,引入了模块化系统。通过模块化,Java本身也得以进行模块化改进。
5.1 模块化是什么
1)相对于JDK8的变动
JDK9以后引入了一个新组件module:模块描述符module-info.java,用于将一组相关的包放入一个组中。
在Java8和更早的应用程序中,应用程序将包作为顶级组件,Java9以后应用程序将模块作为顶级组件。
一个模块(Jar包)只能有一个module-info.java。
2)和maven的关系
3)如何兼容旧应用
天然兼容旧应用。为了向后兼容旧项目,一些库本身并未模块化,其仍然可以作为模块在模块路径中使用,而这些库在模块路径上时会被转化为自动模块,例如:jackson-databind-1.0.0.jar将成为自动模块jackson.databind。
5.2 带来了哪些好处
5.3 如何使用
包结构如下:
com.jdt.a
person
Men.java
reflect
ReflectModel.java
module-info.java
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 module.b {
//依赖a下的包
requires module.a;
}
5.4 实践过程的坑
1)依赖JSF包时无法模块化
进行模块化时,pom中依赖了jsf包,模块定义如下:
module module.a {
requires fastjson;
//依赖jsf包名
requires jsf.lite;
exports com.jd.jdk.test.module;
}
问题原因:
经过一系列定位研究,发现jsf-lite包中,/META-INF/services下的文件org.glassfish.jersey.internal.spi.AutoDiscoverable里面写的类是com.alibaba.fastjson.support.jaxrs.FastJsonAutoDiscoverable,此类并未在当前jsf.lite包中定义,属于com.alibaba.fastjson包的。
解决方案:
例如:
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),不能跨模块读取。
解决方案:
5.5 模块化落地总结
目前不推荐使用模块化,因为相关组件生态还不完善,并且模块化带来的价值不够突出:
1.很多中间件都是基于jdk8构建的,都有可能遇到模块化兼容的问题,比如:jsf,需要jsf强制升级才可以使用模块化;
2.拆包问题无法解决,比如:aws-java-sdk-s3、fluent等。
06 总结
理解,首先 MCube 会依据模板缓存状态判断是否需要网络获取最新模板,当获取到模板后进行模板加载,加载阶段会将产物转换为视图树的结构,转换完成后将通过表达式引擎解析表达式并取得正确的值,通过事件解析引擎解析用户自定义事件并完成事件的绑定,完成解析赋值以及事件绑定后进行视图的渲染,最终将目
2、现阶段不推荐使用模块化,但是不用担心会影响JDK11的升级。
希望以上分享可以给大家带来实际的帮助。
ChatGPT的探索与实践
当小白遇到FullGC
618技术大揭秘:Switchquery秒级配置触达平台的设计与实现
求分享
求点赞
求在看
▪功能支撑:会员成长体系、等级计算策略、权益体系、营销底层能力支持
▪用户活跃:会员关怀、用户触达、活跃活动、业务线交叉获客、拉新促活