一、前言
如果你还在使用 JDK8,否曾经因为 OutOfMemoryError 问题而困扰?或者是否觉得 JVM 调优十分复杂?
那么,今天咱们或许可以通过 JDK17 获取到一些灵感。
本文主要记录当前我们公司在推动所有 JDK8 微服务全量升级到 JDK17 过程中遇到的一些问题!
二、为什么升级到 JDK17
作为最前沿技术人,想用最新的技术总是没有错的。JDK17以及相关的组件升级提供很多新的特性,尤其是 GC 提升很大,上线后服务内存占用降低一半左右,等于间接节约了服务器成本,代码运行效率也有一些提升,这可是实打实的降本增效。
另外,这块当然不光是我,连 Spring Boot 都开始要拥护 JDK17 了,下面这一段是 Spring Boot 3.0 的更新日志。
Spring Boot 3.0 requires Java 17 as a minimum version. If you are currently using Java 8 or Java 11, you'll need to upgrade your JDK before you can develop Spring Boot 3.0 applications.
Spring Boot 3.0 需要 JDK 的最低版本就是 JDK 17,如果你想用 Spring Boot 开发应用,你需要将正在使用的 Java 8 或 Java 11升级到 Java 17。
至于升级后是否还有其他好处,我引自 “京东技术” 的文章来给大家聊下:
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 作为最低版本。
三、真的能行?
咱们就不拿一些专业数据了,例如:密封类、空指针异常、yield关键字、换行文本块、record记录类、G1 垃圾收集器、ZGC等做比较了,毕竟在七哥的公粉上,很多都是运维小伙伴,聊太深不一定对大家的胃口。这块官方也会给出对比,感兴趣的小伙伴可以带会从文章底部的文章中去看下!
咱们拿点大家看得懂的数据:
先给出结论:
1、JDK17 相对于 JDK8 和 JDK11,所有垃圾回收器的性能都有很明显的提升,特别是稳定版的ZGC垃圾回收器。
2、不论任何机器配置下,都推荐使用ZGC,ZGC的停顿时间达到亚毫秒级,吞吐量也比较高。
在 JDOS 平台上选择了不同配置的机器(2C4G、4C8G、8C16G),并分别使用 JDK8、JDK11 和 JDK17 进行部署和压测。
整个压测过程限时 60 分钟,用 180个虚拟用户并发请求一个接口,每次接口请求都创建 512Kb 的数据。最终产出不同GC回收器的各项指标数据,来分析 GC 的性能提升效果。
以下是压测的性能情况:
四、你担心的我想到了
目前,Oracle 官方支持的最新 LTS(长期支持)版本是 JDK 17,而 OpenJDK 社区也正在积极开发和维护 JDK17。
因此,如果你考虑升级 Java 版本,并且希望使用一个稳定和可靠的版本,那么建议选择 JDK17
2021年9月,Oracle 宣布 JDK17 可以免费商用,直到下一个 LTS 版本之后继续提供整整一年,同时 Oracle 将继续按照自 Java 9 以来的相同版本和时间表提供GPL下的 Oracle OpenJDK 版本。
2023年9月,OracleJDK 发布了新的LTS版本 JDK21,这就意味着从2024年9月开始,在生产环境使用 OracleJDK17 将需要付费。
敲黑板:
OracleJDK 和 OpenJDK 这两个之间没有真正的技术差别,因为针对 Oracle JDK 构建过程是基于 OpenJDK 的。自从 JDK11 开始,OracleJDK 和 OpenJDK 在功能上基本相同,所以推荐使用 OpenJDK17 或其他开源的 JDK 版本,这些开源版本都是基于 OpenJDK 构建并提供长期支持的,比如:AdoptOpenJDK、RedHatOpenJDK。
五、聊点大家能听懂的
5.1、升级改造步骤
当前我们应用的上下线,底层都是基于 Jenkins 的 CICD 来实现的,一旦升级,相当于整条链路都需要进行改造!
先从 Jenkins 开始,底层镜像不再使用 jnlp,采用单独的 build pod 进行编译;
[root@OPS-middleware /usr/local/src/zhdya/jenkins/jdk17]# cat Dockerfile
FROM openjdk:17-jdk
#FROM jenkins/jnlp-slave:latest
MAINTAINER ZHDYA
# 安装 maven
COPY apache-maven-3.3.9-bin.tar.gz .
RUN tar -zxf apache-maven-3.3.9-bin.tar.gz && \
mv apache-maven-3.3.9 /usr/local && \
rm -f apache-maven-3.3.9-bin.tar.gz && \
ln -s /usr/local/apache-maven-3.3.9/bin/mvn /usr/bin/mvn && \
ln -s /usr/local/apache-maven-3.3.9 /usr/local/apache-maven && \
mkdir -p /usr/local/apache-maven/repo
COPY settings.xml /usr/local/apache-maven/conf/settings.xml
5.2、maven 配置
[root@OPS-middleware /usr/local/src/zhdya/jenkins/jdk17]# cat settings.xml
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<servers>
...此地无银三百两...
</servers>
<mirrors>
...此地无银三百两...
</mirrors>
<profiles>
<profile>
...此地无银三百两...
</profile>
<profile> //新增这部分
<id>jdk-17</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>17</jdk>
</activation>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.compilerVersion>17</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>
<activeProfiles>
<activeProfile>rdc-private-repo</activeProfile>
</activeProfiles>
</settings>
5.3、配置 Java17 运行镜像
[root@OPS-middleware /usr/local/src/zhdya/jenkins/jdk17/appimages]# cat Dockerfile
FROM ubuntu:latest
MAINTAINER ZHDYA
#更新软件源
RUN apt-get update
#安装软件支持
RUN apt-get install curl netbase wget telnet -y
#清理缓存
RUN apt-get clean
#setup language 解决中文乱码
RUN apt-get install fontconfig -y
#设置中文支持
ENV LANG C.UTF-8
#上传并处理二进制jdk17环境包
ADD jdk-17_linux-x64_bin.tar.gz /usr/local/
#setup java env
ENV JAVA_HOME=/usr/local/jdk-17.0.9
ENV JRE_HOME=$JAVA_HOME/jre
ENV PATH=${JAVA_HOME}/bin:$PATH
编译完成,运行下,确认镜像中 Java 的版本信息:
root@bde574e748be:/# java --version
java 17.0.9 2023-10-17 LTS
Java(TM) SE Runtime Environment (build 17.0.9+11-LTS-201)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.9+11-LTS-201, mixed mode, sharing)
六、这部分要记住
6.1、先记住几个网址
记住三个网址就行:下面这个是oracle的
https://www.oracle.com/java/technologies/downloads/#jdk17-windows
另外两个 OpenJDK17 下载:这个是开源的(推荐!)
https://developers.redhat.com/products/openjdk/download
https://jdk.java.net/archive/
6.2、再来记住几个解释
之前就有很多小伙伴来问我,openjdk:17-jdk-slim
,openjdk:17-jdk-alpine
有啥子区别?
openjdk:17-jdk-slim 和 openjdk:17-jdk-alpine这两个镜像都是OpenJDK 17的Docker镜像,但是他们的底层操作系统不同,他们的主要区别在以下几个方面:
-
基础操作系统:openjdk:17-jdk-slim 基于 Debian(slim版)操作系统构建,而 openjdk:17-jdk-alpine 基于 Alpine Linux构建。Alpine Linux是一种轻量级的Linux发行版,相比于Debian,它的镜像大小更小,启动更快。
-
镜像大小:通常,Alpine 基础的镜像会比 Debian slim 基础的镜像更小。这是因为 Alpine Linux 使用了 BusyBox 和 musl libc,这使得其基础系统更小更轻量。
-
二进制兼容性:由于 Alpine Linux 使用的是 musl libc,而不是大多数 Linux 发行版使用的 glibc,这可能导致一些针对 glibc 的二进制程序在 Alpine 上运行出现问题。如果你的应用依赖于某些特定的 glibc 特性,那么使用 Debian slim 可能会更好。
-
包管理:Debian 使用 APT 作为包管理工具,而 Alpine 使用 APK。这可能会影响你在Dockerfile中如何安装软件包。
6.3、如果你是这样用的
如果你的 K8S 是这样用的,修改前:
template:
metadata:
labels:
app: <podname>
spec:
containers:
- env:
- name: LANG
value: C.UTF-8
- name: JAVA_HOME
value: /usr/lib/jvm/java-1.8-openjdk
修改后:
template:
metadata:
labels:
app: <podname>
spec:
containers:
- env:
- name: LANG
value: C.UTF-8
- name: JAVA_HOME
value: /usr/lib/jvm/java-17-openjdk
要使用 JDK17 的 maven,所以要替换一下 JDK8 的 maven 镜像,修改前:
stage("Build") {
agent {
docker {
image 'maven:3-alpine'
args '-v /root/.m2:/root/.m2'
}
}
}
修改后:
stage("Build") {
agent {
docker {
image 'maven:3.8.4-openjdk-17-slim'
args '-v /root/.m2:/root/.m2'
}
}
}
七、总结
JDK 17 也提供了广泛的操作系统、硬件平台和云平台支持,可以适用于各种不同的应用场景。
建议选择 JDK 17 作为升级目标,由于 JDK 17 是一个 LTS 版本,因此可以获得更长时间的更新和支持,从而提供更好的稳定性和安全性。