升级到springboot3与jdk21需要做的工作

随着springboot3的正式版推出,最小可用的jdk版本为17。最大的亮点是颠覆性的zgc,可以将stw时间控制在亚毫秒级别,接着jdk21推出了分代zgc。根据jdk官方说法,此gc算法适用于任何内存条件下,多核cpu环境。我们也在各个环境的生产系统做到了验证(2c4g、4c8g、8c16g)。对于中间件,rocketmq、kafka、es、nacos等。因所用jdk版本尚未完全支持,且目前也没有遇到过问题,暂未做升级。

对于新项目来说,springboot3+jdk21已经可用,基本上不会有问题。

对于springboot2+jdk8项目的升级,也相对较简单。

升级步骤

1、jdk我们选择zulu21 https://www.azul.com/downloads/#zulu

2、pom配置统一到21

1
2
3
4
<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
</properties>

3、从2.7.x升级springboot到最新版本,根据官方的Migration Guide升级即可 https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Migration-Guide

4、循环依赖,某些项目目前碰到了此类情况。修改配置即可

1
spring.main.allow-circular-references=true

5、lombok版本升级到最新

6、找不到相关的类,一般是因为从jdk11开始删除了j2ee相关的类。根据报错引入jakarta相关包即可,代码中将j2ee相关代码,改为使用jakarta

7、某些应用,会使用到反射。新jdk版本中因为安全问题,做了一些限制。目前做得比较粗暴,在jvm中增加如下配置

1
--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED

8、swagger改为使用springdoc-openapi 依赖,并做相应的代码修改

9、docker打包基础镜像修改为统一使用zulu21

升级后,所有生产环境jvm配置统一修改

以下是用得最多的4c8g配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-Xmx6g -Xms6g #堆内存初始化6g
-XX:+UseZGC #使用zgc
-XX:-ZUncommit #关闭将不使用的内存返回给操作系统
-XX:+UseDynamicNumberOfGCThreads #当开启时,ZGC 会根据GC运行状态(例如GC耗时、堆空余空间、对象分配频率等)由内置的启发式算法自动选择并发阶段的 GC 线程数量(最小为 1,最大为 -XX:ConcGCThreads)。当关闭时,则会固定使用 -XX:ConcGCThreads 数量的线程。
-XX:+ZGenerational #启用jdk21生产可用的分代zgc算法,会在gc的时候消耗更多的cpu
-XX:+AlwaysPreTouch #启用内存预分配,需要内核版本4.3+
#gc日志记录到/opt/logs/文件名为启动的时间戳,每个日志记录包含时间戳、日志级别、线程id、标签。文件大小限制到50M,达到后创建新的文件继续记录
-Xlog:gc*=info:file=/opt/logs/gc-%t.log:time,level,tid,tags:filesize=50M  
#发生OOM时dump内存日志
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/apps/errorDump.hprof

因为目前公司的java服务均跑在容器内部,Huge Pages、NUMA Support等未做开启

总结

本文探讨了如何升级到jdk21,并使用最新的分代zgc。jdk21也带来了很多简便的新语法和适合IO密集的虚拟线程。虚拟线程相比于平台线程在某些场景下优势明显。只是需要注意仅使用可重入锁对象,而不使用synchronize关键字(反而降低性能)。相关资料:https://openjdk.org/jeps/444

经过实践和测试,我们发现升级后的系统在垃圾回收方面表现出色,暂停时间确实如官方所说被控制到了亚毫秒内。尽管这个优化会额外消耗cpu资源,但获得了超低stw时间,权衡这下显然非常值得。

相比其他已经经过生产环境调优的垃圾回收器(G1、Parallel、CMS),ZGC的性能和稳定性都非常优秀,几乎不需要太多额外的调优,就可以得到响应时间的降低和吞吐量的增加。即使在以前吞吐量有优势的Parallel,依然具有优势。

ZGC可以统一江湖了!另据得到的消息,某些大厂主要服务均升级到了jdk21。但也进一步说明了,(分代)zgc和虚拟线程的强大。你们还在用jdk8吗?

更新

新增一些中大厂使用jdk17/jdk21和zgc的案例,作为以后项目的参考

References

Spring-Boot-3.0-Migration-Guide:迁移到springboot3

ZGC

转转:“分代ZGC在转转商列服务中的实践”

得物:“亚毫秒GC暂停到底有多香?JDK17+ZGC初体验|得物技术”

京东:“JDK11升级JDK17最全实践干货来了”

京东营销:“JDK 17 营销初体验 —— 亚毫秒停顿 ZGC 落地实践”

美团: “新一代垃圾回收器ZGC的探索与实践”

阿里云:“The Past and Present of JDK8 and JDK17 to the Future of JDK21”

Automq:Java ZGC 深度剖析及其在构建低延迟流系统中的实践心得

Talk is cheap, show me the bug/code.
使用 Hugo 构建
主题 StackJimmy 设计