每次在虚拟机里给 CentOS 7 装 JDK,总有人卡在"到底用 yum 还是手动解压"这一步。前阵子帮同事在生产测试机上配 Java 环境,从 VMware 装 CentOS 7 一路折腾到 OpenJDK 11 落地,中间踩了几个不大不小的坑,索性把整个过程梳理成一篇实操记录。CentOS 7 和 OpenJDK 11 这套组合在不少存量项目里还很常见,尤其是那些跑着 Spring Boot 2.x、Elasticsearch 7.x 或者 Hadoop 3.x 的机器,JDK 版本基本都锁在 8 或 11 上。这篇文章面向两类人:一类是刚接触 Linux、想在虚拟机里练手的小白,需要一条能照着敲的命令路径;另一类是手上有几台老机器、需要批量把 JDK 从 8 升到 11 的运维同学,更关心目录规范、多版本切换和离线安装。下面两种安装方式我都会给全,一种走 yum 包管理器,一种走 tar.gz 手动解压,各自的适用场景、目录结构差异、环境变量写法都会讲透,最后附一份报错速查表和几条我自己的使用习惯。先说明,所有命令都在 CentOS 7.9 最小化安装环境下实测过,x86_64 架构,普通用户切换 root 执行。
1. 装之前先想明白:为什么是 OpenJDK 11
1.1 OpenJDK 和 Oracle JDK 差的到底是什么
很多人第一次接触这个概念会懵,其实两者共享同一套 Java 规范,核心的虚拟机(HotSpot)、编译器(javac)和标准库源码几乎是同源的。真正拉开差距的是三块:许可证、更新节奏和商业附加组件。OpenJDK 走的是 GPLv2 + Classpath Exception 的开源协议,拿到手就能商用、能改、能重新分发,不需要为每台机器单独谈授权。Oracle JDK 从 11 开始转向 OTN 协议,生产环境商用需要付费订阅,这在公司采购流程里是个绕不开的坎。至于字体渲染、Java Flight Recorder、Java Mission Control 这些原来只在 Oracle JDK 里完整提供的东西,OpenJDK 11 阶段已经基本补齐,日常做后端服务的体感差异几乎为零。
我更愿意把 OpenJDK 理解成"上游主干",Oracle JDK 更像是"官方发行版",后者在前者基础上打包了一些企业级工具。对于跑微服务、写业务代码的场景,OpenJDK 完全够用,而且升级、打补丁的路径更清爽。
1.2 为什么卡在 11 而不是 8 或者 17
JDK 8 到现在还活着,是因为大量老框架还依赖它。但 JDK 8 有几个绕不开的痛点:默认 GC 还是 Parallel,局部变量类型推断(var)没有,模块化没有,容器感知能力差——在 Docker 里跑很容易因为看不到 cgroup 限制而把内存吃爆。JDK 11 是继 8 之后的第一个 LTS 版本,长期支持到 2026 年以后,容器感知(UseContainerSupport)默认打开,G1 成为默认垃圾回收器,还引入了 ZGC 的实验性版本,对内存不大的虚拟机挺友好。JDK 17 虽然更新,但很多老项目用的 Lombok、某些字节码增强框架在 17 上还要调参数,迁移成本明显高一截。所以"存量机器升到 11"是性价比最高的选择,既拿到了容器时代的基础设施红利,又不用大改业务代码。
1.3 两种安装方式各自的脾气
yum 装法本质上是把 JDK 当成系统 RPM 包管理,优点是一条命令搞定、依赖自动解决、升级用yum update就能跟进,缺点是版本被仓库锁死,目录结构是发行版自定义的,想精确控制路径不太方便,而且 CentOS 官方源里 JDK 版本相对滞后。手动解压则是把官方或社区构建的 tar.gz 丢到指定目录,环境变量自己配,版本随便选,多版本共存也好管理,代价是每一步都得自己来,升级要去手动替换目录。
我的判断标准很粗暴:如果这台机器是临时测试、学练手、跑个 demo,yum 装完拉倒;如果是生产服务器、需要精确控制 JDK 版本和补丁、或者有多套应用要跑不同 JDK,那就手动解压,把控制权攥在自己手里。下面两种都完整走一遍。
2. 动手前的准备:虚拟机、系统与旧环境清理
2.1 VMware 里装 CentOS 7 的几个关键选择
不管你是用 VMware Workstation 还是别的虚拟化工具,新建虚拟机时这几个选项会直接影响后面装 JDK 的顺利程度。第一是内存,CentOS 7 最小化安装跑起来 1GB 勉强够,但你要在上面编译、跑 Java 程序,建议直接给 2GB 起步,装 Elasticsearch 这类吃内存的组件就往上加。第二是磁盘,默认 20GB 够了,但如果打算留多份 JDK 备份和快照,给到 40GB 更从容,选择"拆分成多个文件"方便迁移。第三是网络模式,桥接还是 NAT 看你的网络环境,关键是要能连外网,因为 yum 方式需要访问软件源,手动方式也需要下载安装包。
安装系统时选"最小安装"就好,不要勾选带 GUI 的选项,图形界面在服务器上纯属浪费内存。安装完第一件事是配置网络,vi /etc/sysconfig/network-scripts/ifcfg-ens33把ONBOOT=yes打开,重启网络服务。这一步做完,ping一下外网,确保后面 yum 能正常工作。
2.2 确认系统版本和 CPU 架构
装 JDK 之前必须确认两件事:系统大版本和 CPU 位数。命令很简单:
cat /etc/redhat-release uname -m第一条会输出类似CentOS Linux release 7.9.2009 (Core),确认是 7.x 而不是 8。第二条输出x86_64就是 64 位,现在基本见不到 32 位服务器了,但虚拟机里偶尔有人装成 i686,那就得换对应的 JDK 包。为什么要确认这么细?因为 OpenJDK 的安装包是按架构分发的,下错架构的包解压出来java -version直接报无法执行二进制文件,排查起来很费劲。
顺手把系统更新到最新,yum update -y,尤其是最小化安装的系统,仓库元数据可能比较旧,不更新后面yum search可能搜不到 java-11 的包。
2.3 把机器里残留的 JDK 清干净
如果这台机器之前装过 JDK,不管是通过 yum 还是手动解压,都要先清掉,不然环境变量指向的可能是旧版本,造成"我明明装了 11,java -version还是 1.8"这种经典问题。先用两条命令摸清楚现状:
rpm -qa | grep -i jdk which java第一条列出所有通过 RPM 装的 JDK 相关包,包括java-1.8.0-openjdk、java-1.7.0-openjdk-headless之类的。第二条看当前java命令走的是哪个路径,如果是/usr/bin/java,大概率是系统自带的或 yum 装的。
清理 rpm 包用yum remove,把列出来的包名一个个删掉或者一次性删:
yum remove java-1.7.0-openjdk* java-1.8.0-openjdk* -y如果是手动解压装的,找到原来的目录(通常在/usr/local/或/opt/下),把目录删掉,然后把/etc/profile、~/.bash_profile、/etc/profile.d/里配置的JAVA_HOME和PATH相关行注释或删除。
注意:删除环境变量时别手抖把整个 profile 文件清空了,先备份一份
cp /etc/profile /etc/profile.bak再动手,改坏了解不了系统就麻烦了。
3. 方式一:yum 包管理器安装,图个省心
3.1 先确认仓库里到底有没有 11 的包
CentOS 7 官方 Base 源在 7.6 之前只带 JDK 8,7.6 以后陆陆续续补了 java-11。所以第一步是搜一下:
yum list available | grep -i openjdk或者更精准一点:
yum search java-11-openjdk如果输出里能看到java-11-openjdk.x86_64、java-11-openjdk-devel.x86_64这类条目,说明源里有,直接装就行。如果什么都没搜到,有两个可能:一是系统太老,Base 源里确实没有;二是仓库元数据没刷新,先跑yum makecache再搜。实在没有的话,可以临时挂载一个第三方的仓库配置,但这一步涉及信任来源问题,我更建议直接走手动解压方式,干净可控。
这里要区分两个包:java-11-openjdk是运行时环境(JRE),java-11-openjdk-devel是开发工具包(JDK),里面才有javac、jps、jstack这些工具。如果你要在机器上编译代码,一定要装 devel 版;如果只是运行已经打好的 jar 包,装前者就够。生产环境我一般都装 devel,多占那几十兆换个安心。
3.2 一条命令装完并验证
确认有了之后,安装命令很直接:
yum install java-11-openjdk java-11-openjdk-devel -y装完立刻验证:
java -version javac -version正常应该输出类似:
openjdk version "11.0.21" 2023-10-17 LTS OpenJDK Runtime Environment (Red_Hat-11.0.21.0.9-1.el7_9) (build 11.0.21+9-LTS) OpenJDK 64-Bit Server VM (Red_Hat-11.0.21.0.9-1.el7_9) (build 11.0.21+9-LTS, mixed mode, sharing)看到11.0.x且带 LTS 字样就成了。如果只显示javac找不到,说明只装了 JRE 没装 devel,补一条yum install java-11-openjdk-devel -y即可。
3.3 yum 装的目录结构长什么样
yum 方式最大的特点是目录被拆散了,跟手动解压完全不是一个布局。用rpm -ql java-11-openjdk能看到文件清单,核心位置大致是:
- 可执行文件软链在
/usr/bin/java、/usr/bin/javac - 真正的运行时在
/usr/lib/jvm/java-11-openjdk-11.0.21.0.9-1.el7_9.x86_64/ - 配置文件在
/etc/java-11-openjdk/ - 系统级环境变量入口在
/etc/profile.d/java-11.sh(不一定有,看版本)
这就带出一个关键问题:JAVA_HOME到底该指向哪?答案是/usr/lib/jvm/java-11-openjdk-xxx,但那个目录名带小版本号,升级之后目录名会变,写死在环境变量里是个隐患。更稳的做法是用/usr/lib/jvm/java-11-openjdk这个不带小版本号的软链,或者干脆用alternatives机制。
alternatives --config java这段命令会列出系统里所有已注册的 java 可执行文件,让你用数字选择默认版本。yum 装的 JDK 会自动注册到 alternatives 里,所以多版本切换特别省事。这也是我推荐测试机用 yum 的核心原因:版本切换不用改一堆配置文件,一条命令搞定。
3.4 这种方式什么时候别用
yum 装的 JDK 版本跟随仓库走,补丁节奏由发行版决定。有些安全要求高的场景,需要指定某个精确的 JDK 小版本(比如 11.0.17 而不是 11.0.21),yum 满足不了。另外某些第三方中间件对 JDK 目录结构有硬性要求(比如要求$JAVA_HOME/jre/lib这种 JDK 8 遗留结构),yum 装的 11 里没有独立的jre目录,需要额外处理。碰到这类情况,直接转手动解压。
4. 方式二:tar.gz 手动解压,把控制权拿回来
4.1 安装包从哪来,怎么校验
手动安装的第一步是拿到 tar.gz 包。常见来源有两类:一类是各发行版打包好的 openjdk 二进制,另一类是社区构建的发行版本,比如 Eclipse Temurin、Amazon Corretto、Azul Zulu 等。选哪个看你的信任链和合规要求,我个人在内部环境习惯用发行版打包版本,跟系统库的兼容性最稳。
下载下来之后一定要校验完整性,这一步经常被跳过,但网络传输中断导致的半截包解压报错能让你排查半天。用 SHA256 比对:
sha256sum OpenJDK11U-jdk_x64_linux_hotspot_11.0.21_9.tar.gz把输出的哈希值跟官方页面公布的对比,一致才继续。如果是在内网、无法直连外网下载,就在本地下载好后通过 scp 或共享目录传上去:
scp OpenJDK11U-*.tar.gz root@192.168.1.100:/tmp/注意:安装包传上去后别急着重命名,很多脚本会按原始文件名解析版本,改了名虽然解压没问题,但后续自动化逻辑可能识别不到。
4.2 解压到哪、目录怎么规划
目录选择上我坚持一条原则:JDK 这类基础设施放在/usr/local/下,不用/opt/,也不用 home 目录。/opt更适合放第三方应用本身,/usr/local是惯例上放本地编译安装的软件。具体路径建议带上版本号:
mkdir -p /usr/local/java cd /usr/local/java tar -zxvf /tmp/OpenJDK11U-jdk_x64_linux_hotspot_11.0.21_9.tar.gz解压出来通常是jdk-11.0.21+9这样的目录。为了后续环境变量稳定、升级方便,建一个不带版本的软链指向它:
ln -s /usr/local/java/jdk-11.0.21+9 /usr/local/java/current以后升级只要换掉软链指向就行,环境变量完全不用动。这个技巧能省掉大量重复配置的工作,多台机器批量维护的时候尤其明显。
4.3 环境变量到底该写在哪
这是最容易出问题的环节。环境变量可以写好几处,优先级和加载时机都不一样:
/etc/profile:全局,所有用户登录时加载,适合全机器统一的配置/etc/profile.d/*.sh:同样是全局,但分文件管理,更清爽,推荐用这个~/.bash_profile:只对当前用户生效~/.bashrc:交互式 shell 加载,登录 shell 不一定读
我的习惯是在/etc/profile.d/下单独建一个java.sh:
vi /etc/profile.d/java.sh内容写:
export JAVA_HOME=/usr/local/java/current export JRE_HOME=${JAVA_HOME} export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:${PATH}保存后让配置立即生效:
source /etc/profile.d/java.sh这里有个细节要讲清楚:PATH里把${JAVA_HOME}/bin放在最前面,是为了让这个 JDK 优先于系统自带的/usr/bin/java。如果不放最前面,which java可能还是指向旧版本。
4.4 用 alternatives 管多版本共存
如果机器上不止一个 JDK,光靠 PATH 顺序容易乱。更规范的做法还是用alternatives。手动装的 JDK 不会自动注册,需要自己加:
alternatives --install /usr/bin/java java /usr/local/java/current/bin/java 2000 alternatives --install /usr/bin/javac javac /usr/local/java/current/bin/javac 2000末尾那个2000是优先级,数字越大越优先。然后切默认版本:
alternatives --config java会看到一个列表,输入对应数字回车即可。这样切换版本不需要 source 任何文件,对运维来说干净利落。javac同理,记得一起注册,否则会出现java是 11 而javac还是 8 的割裂状态。
4.5 最终验证别只看 java -version
装完之后除了java -version,我还会顺手跑几个检查:
which java echo $JAVA_HOME java -XshowSettings:properties -version 2>&1 | grep -i "java.home"第三条会打印 JVM 实际加载的java.home路径,能确认环境变量和实际运行是否一致。全部对得上,这套环境才算真正落地。
提示:验证时开一个新的终端窗口再跑一遍,因为环境变量在旧窗口里可能是缓存过的,新窗口才能反映真实登录加载情况。
5. 踩坑实录:报错和排查思路
5.1 java 命令找不到,或者版本对不上
command not found十有八九是 PATH 没生效。先echo $PATH看里面有没有${JAVA_HOME}/bin。没有的话,检查/etc/profile.d/java.sh是否存在、是否有语法错误(比如漏了引号)。可以用bash -x /etc/profile.d/java.sh看执行过程有没有报错。
版本对不上则是 PATH 顺序问题,which -a java能列出所有能找到的 java,看排在第一位的是不是你想要的。如果首位是/usr/bin/java,说明有 yum 装的旧版本还在,用 alternatives 切过去或者卸载旧包。
5.2 环境变量改了没生效
这种情况通常是改错了文件,或者改完没重新登录。~/.bashrc里的配置在非交互式登录时不加载,如果你是通过某些自动化工具连上去执行命令的,它可能读的是/etc/profile而跳过了~/.bash_profile。所以全局配置我永远推荐放/etc/profile.d/,兼容性最好。另外,用source只对当前 shell 有效,已经开着的其他窗口需要重新source或者退出重登。
5.3 多版本切换后残留问题
切了 alternatives 之后java -version变了,但某些应用启动脚本里硬编码了旧路径,这就不是环境变量能覆盖的了。排查思路是ps -ef | grep java看进程实际的启动命令,或者查应用自己的配置(如 Tomcat 的setenv.sh、systemd 的Environment=)。这种硬编码在接手别人维护的机器时特别常见。
5.4 常见问题速查表
| 现象 | 可能原因 | 处理方式 |
|---|---|---|
java: command not found | PATH 未包含 JDK bin | 检查/etc/profile.d/java.sh并 source |
java -version显示 1.8 | 旧版本优先或未切换 | 用 alternatives 切换,卸载旧包 |
解压报gzip: stdin: not in gzip format | 包下载不完整或下错 | 重新下载并校验 SHA256 |
应用启动报错找不到jre | JDK 11 无独立 jre 目录 | 用$JAVA_HOME替代,或建软链兼容 |
| 环境变量新窗口生效旧窗口不生效 | shell 缓存 | 关掉旧窗口重开,或手动 source |
javac找不到 | 只装了 JRE | 补装 devel 包 |
6. 几个我自己常用的习惯和小细节
6.1 目录规划固定下来,别每次都不一样
新人最容易犯的毛病是这台机器 JDK 装在/usr/local/java,那台装在/opt/jdk,第三台直接解压到 root 家目录。机器一多,排查问题时光是找目录就得花半天。我给自己定了一套规矩:所有手动装的 JDK 统一放/usr/local/java/,版本目录带完整版本号,再用current软链指向当前使用的版本。这样无论看哪台机器,路径结构都一致,写自动化脚本也好写。
6.2 装之前先给虚拟机打个快照
这条在虚拟机环境里特别值。不管是 VMware 还是其他平台,动手装 JDK 之前先打个快照,出问题回滚几秒钟。我见过有人环境变量文件改坏了导致 root 登录直接卡住,又不会进单用户模式解救,最后整台机器重装,何必呢。快照不占多少空间,能省掉最坏情况下几个小时的恢复时间。
6.3 生产环境到底该怎么选
给个我自己的决策表:临时测试、学习环境,yum 装,省事且切换方便;正式生产、要求版本锁定,手动解压,用软链管理升级;容器化环境,直接在 Dockerfile 里用一个固定的基础镜像解压安装,比在容器里跑 yum 更可控,镜像层也更小。还有一个思路值得提——如果公司有多台机器要统一 JDK 版本,与其每台手动解压,不如把解压好的目录打包成内部 RPM 包,这样既保留了版本可控,又能享受 yum 的批量分发能力。这个做法我用过几次,维护成本比纯手动低很多。
6.4 升级 JDK 时的最小影响流程
最后分享一个升级小版本时我常用的操作顺序。先下载新版本包解压到/usr/local/java/jdk-11.0.22+8,然后改软链指向新目录:ln -sfn /usr/local/java/jdk-11.0.22+8 /usr/local/java/current。改完之后不要急着重启应用,先java -version确认软链生效。确认无误后再逐个重启应用,观察一段时间没问题,旧的版本目录先留着,等稳定运行一周再清理。这样任何一步出问题都能快速回退,比直接覆盖目录安全得多。