外观
JDK 安装与多版本切换
生产机器上同时存在 3 个 JDK 版本是常态:跑业务用 17、编译老项目用 8、某个中间件自带一个。最忌讳的是把 JAVA_HOME 写死到具体版本号目录,升级时全线崩。
一、tar 包安装(推荐)
发行版包管理器装的 JDK 会往 /usr/lib/jvm 塞一堆软链,且版本更新不可控。手工解压最干净。
bash
mkdir -p /opt/java && cd /opt/java
tar zxf jdk-17.0.11_linux-x64_bin.tar.gz
tar zxf jdk-8u421-linux-x64.tar.gz
ls -1 /opt/java输出:
text
jdk1.8.0_421
jdk-17.0.11解释:解压到哪不重要,重要的是统一放一个父目录,后面用软链切换。/opt 是第三方软件的规范位置,不跟系统包管理器抢 /usr。
二、用软链做版本解耦
bash
ln -sfn /opt/java/jdk-17.0.11 /opt/java/current然后所有地方只引用 /opt/java/current:
bash
# /etc/profile.d/java.sh
export JAVA_HOME=/opt/java/current
export PATH=$JAVA_HOME/bin:$PATHbash
source /etc/profile.d/java.sh
java -version输出:
text
openjdk version "17.0.11" 2024-04-16 LTS
OpenJDK Runtime Environment (build 17.0.11+7-LTS-207)
OpenJDK 64-Bit Server VM (build 17.0.11+7-LTS-207, mixed mode, sharing)解释:换版本只需 ln -sfn /opt/java/jdk1.8.0_421 /opt/java/current,不用改任何脚本和 systemd 文件。这是本章最重要的一条约定。
source 只影响当前 shell
source /etc/profile.d/java.sh 改不了已经启动的进程,也不影响别的 SSH 会话。systemd 服务读不到 /etc/profile,必须在 unit 里显式写 Environment=(见"服务托管"一节)。
三、给单个应用固定版本
不要让应用继承全局 JAVA_HOME——全局一改它就跟着变。
ini
# /etc/systemd/system/order.service 片段
[Service]
Environment=JAVA_HOME=/opt/java/jdk-17.0.11
ExecStart=/opt/java/jdk-17.0.11/bin/java -jar /opt/app/current/app.jar解释:显式写全路径最保险。Environment= 只给这个服务,不影响别人。
四、alternatives 方式(RHEL 系)
适合"系统级默认版本"的切换,比如 javac 给命令行用:
bash
alternatives --install /usr/bin/java java /opt/java/jdk-17.0.11/bin/java 1700
alternatives --install /usr/bin/java java /opt/java/jdk1.8.0_421/bin/java 1800
alternatives --config java输出:
text
Selection Path Priority Status
------------------------------------------------------------
* 1 /opt/java/jdk-17.0.11/bin/java 1700 auto mode
2 /opt/java/jdk1.8.0_421/bin/java 1800 auto mode解释:优先级数字大的自动胜出,所以 JDK 8 反而成了默认——这是个常见的坑,命名优先级时要按"越大越新"的直觉反着留意。--config 是交互式选择,脚本里别用。
五、验证 JAVA_HOME 到底生效在哪
bash
echo $JAVA_HOME
readlink -f "$(which java)"
java -XshowSettings:properties -version 2>&1 | grep -E 'java.home|java.version'输出:
text
/opt/java/current
/opt/java/jdk-17.0.11/bin/java
java.home = /opt/java/jdk-17.0.11
java.version = 17.0.11解释:三者不一致就是有问题——which java 命中的可能是 /usr/bin/java(系统包管理器装的),而不是你 PATH 里的那个。这时检查 echo $PATH 顺序,或直接 hash -r 清掉 shell 的命令路径缓存。
六、JDK 版本选择速查
| 版本 | 状态 | 建议 |
|---|---|---|
| 8 | 商用授权受限(Oracle),开源侧用 Temurin | 存量项目继续用,新项目别选 |
| 11 | LTS,支持到 2032(各厂商不同) | 老项目升级的稳妥中间档 |
| 17 | LTS | 当前主流,Spring Boot 3 的最低要求 |
| 21 | LTS | 虚拟线程值得为它升级 |
| 25+ | 非 LTS | 只在能持续跟进的短周期项目上用 |
换发行版别忘 GC 默认值变了
JDK 8→11 默认仍是 Parallel GC;11 起 G1 成为默认;17 之后分Region 策略和停顿目标实现都有变化。同一段代码换 JDK 后 GC 日志形态会变,别以为"参数没改行为就不变"。