ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

CentOS 7 安装 OpenJDK 11 的 yum 与 tar.gz 方式

CentOS 7 安装 OpenJDK 11 的 yum 与 tar.gz 方式 折腾 CentOS 7 上的 Java 环境几乎是每个后端或者运维同学的入门必修课。我自己的习惯是每当新开一台测试机或者刚在 VMware 里装完 CentOS 7 虚拟机第一件事就是把运行环境搭起来而 OpenJDK 11 是这几年用得最顺手的一个版本——它是长期支持版本社区维护稳开源协议干净绝大多数中间件和框架都把它当成默认基线。问题在于CentOS 7 自带的 yum 源里默认给的是 OpenJDK 8想上 11 得自己想办法。这篇文章就把我常用的两条路完整拆开讲一条是用 yum 直接在线装官方仓库里的 OpenJDK 11省事省心另一条是下载 tar.gz 压缩包手动解压安装灵活可控、能精确到具体小版本也方便一台机器上并存多个 JDK。两条路各有各的适用场景我会把每一步命令、每一个参数背后的理由、以及我实际踩过的坑都写清楚不管你刚接触 Linux 还是已经写过几年脚本照着做都能落地。1. 为什么是 OpenJDK 11 而不是直接沿用手上的 JDK 81.1 两条安装路线的取舍逻辑先说说为什么会有两种方式这个问题。CentOS 7 是 2014 年发布的发行版默认仓库里长期只有 OpenJDK 8 系列而 OpenJDK 11 是在 2018 年 9 月正式发布的。两者之间隔了整整一代CentOS 7 的官方源为了保持稳定性不会随便把大版本往上抬。于是摆在面前的选项就有两类要么去补一个包含 OpenJDK 11 的软件源让 yum 帮你搞定依赖和升级要么绕开包管理器直接把官方的二进制压缩包解压到某个目录自己接管环境变量。这两条路线的本质区别在于谁来管理这个 JDK 的生命周期。走 yum包的安装路径、软链接、alternatives 切换机制、卸载逻辑都由 RPM 体系托管好处是干净、可追溯坏处是版本被仓库锁死仓库什么时候更新、更新到哪个小版本你说了不算。走 tar.gz目录是你自己选的小版本是你自己挑的升级就是换个目录改一行环境变量坏处是所有事情都得手动来卸载的时候也要记得清理干净不然容易出现明明改了 JAVA_HOME 但 java -version 还是旧的这种诡异现象。我在实际项目里的判断标准很简单如果是短期测试机、CI 构建节点、或者不想在这台机器上花太多心思的环境直接走 yum如果是需要精确控制 JDK 小版本的生产环境、需要跑特定发行版比如带某些性能补丁的构建、或者一台机器要同时跑 JDK 8 和 JDK 11 两套服务那就走 tar.gz。这个判断标准后面还会再展开先记着。1.2 版本号背后的坑目录命名和 JAVA_HOME 的关系在动手之前有个细节必须先说清楚因为它会让至少一半的新手在原地卡住半小时。OpenJDK 11 的版本号写法有几个不同的面孔你在不同地方看到的可能是完全不一样的字符串官方发布名是11.0.20、11.0.21这种形式yum 包名写的是java-11-openjdk-11.0.20.1.1.el7_9.x86_64解压出来的目录名可能是jdk-11.0.20、jdk-11.0.208、amazon-corretto-11.0.20.8.1-linux-x64RPM 安装后的实际路径长这样/usr/lib/jvm/java-11-openjdk-11.0.20.1.1.el7_9.x86_64/。而JAVA_HOME必须指向一个真实存在的绝对路径不能写java、不能写/usr/bin/java、更不能指望它自己识别。很多人习惯性把JAVA_HOME配成/usr/bin/java或者在/usr/lib/jvm/下随便挑一个看着像的目录名填进去结果就是 Maven 报JAVA_HOME is set to an invalid directoryTomcat 启动脚本直接退出。注意/usr/bin/java是一个软链接最终指向的可能是/etc/alternatives/java再指向真实 JDK 的bin/java。把JAVA_HOME指向软链接的上一层是错的必须指向 JDK 的根目录也就是包含bin、lib、conf这几个子目录的那一层。这也是为什么我强烈建议在配置之前先跑一条readlink -f $(which java)看清这个 java 命令到底落的哪个真实文件然后把路径里最后的/bin/java砍掉剩下的就是JAVA_HOME。这个动作花不了十秒钟能省掉后面一大堆排查时间。2. 动手前的环境确认与准备工作2.1 三条命令摸清系统底细不管走哪条路装之前先把家底摸清楚。我固定会跑这三条命令cat /etc/redhat-release uname -m getconf LONG_BIT第一条告诉你具体的 CentOS 小版本比如CentOS Linux release 7.9.2009 (Core)。这条信息决定了你在补软件源时要选哪个 releasever也决定了某些包名后缀是el7还是el7_9。第二条和第三条一起看输出x86_64和64说明是 64 位系统。如果你在 VMware 里建虚拟机时手滑选了 32 位或者装的镜像是 i686 版本后面下载压缩包时会直接下错跑起来报cannot execute binary file。顺手再确认一下磁盘空间和内存df -h /usr/local /opt free -hOpenJDK 11 本身解压后大概 300MB 左右真正吃空间的是后续的 Maven 本地仓库、日志和堆转储文件所以我一般会给/usr/local或者/opt留出至少 5GB 的余量。内存这块CentOS 7 在 VMware 里跑我建议至少给 2GB要编译打包的话给到 4GB 更舒服因为 JVM 默认的初始堆大小是物理内存的 1/64最大堆是 1/4小内存机器上编译大项目很容易触发 GC 抖动甚至 OOM。2.2 清理旧 JDK 残留避免影子版本第二件事是查一遍系统里已经存在什么 JDK。CentOS 7 最小化安装通常不带 Java但如果你用的是带 GUI 或者带开发工具的镜像很可能已经装了 OpenJDK 8甚至可能装了不止一套。rpm -qa | grep -i -E jdk|java这条命令会列出所有通过 RPM 安装的 Java 相关包。如果输出里出现了java-1.8.0-openjdk、java-1.8.0-openjdk-headless、java-1.8.0-openjdk-devel这些你要先想清楚这台机器上的其他软件是否依赖它们比如某些系统工具、Ansible、Jenkins agent 可能默认吃 JDK 8。如果确认没有依赖或者你就是要做一次干净的版本替换那就卸载掉yum remove -y java-1.8.0-openjdk*卸载完再跑一次java -version如果还能输出说明有手动安装的 JDK 残留在环境变量里。这时候去看这几个文件ls -l /etc/profile.d/ grep -rn JAVA_HOME\|PATH /etc/profile /etc/profile.d/ ~/.bashrc ~/.bash_profile 2/dev/null这一步我踩过坑。有一次在别人搭好的机器上装 JDK 11装完之后java -version死活显示 1.8which java指向/usr/local/jdk1.8/bin/java翻遍了/etc/profile都没找到最后发现是~/.bashrc里藏了一行 export而当时是用su切过去的走的又是另一套加载逻辑。所以清理这一步一定要查全/etc/profile.d/下的自定义脚本、/etc/profile、~/.bashrc、~/.bash_profile这四处都要看。2.3 VMware 虚拟机上的两个额外准备如果你是在本机 VMware 里新装的 CentOS 7有两个小动作建议提前做掉能省很多事。第一是打快照。装 JDK 本身风险不大但配环境变量时手滑改错/etc/profile导致 SSH 登不进去、只能进单用户模式救场的经历我遇到过不止一次。装之前打个干净快照出问题直接回滚比修一晚上划算得多。第二是网络。yum 安装这条路必须联网VMware 虚拟机建议把网络模式设成 NAT这样即使宿主机切换了网络环境比如从家里到公司虚拟机也不用重新配 IP。装之前先测一下ping -c 3 mirrors.aliyun.com能通说明 DNS 和出网都正常。如果 ping 域名不通但 ping IP 通那就是/etc/resolv.conf里的 DNS 配置问题先解决这个再往下走不然后面yum install会卡在正在解析主机半天不动。另外提醒一句CentOS 7 已经在 2024 年 6 月停止维护官方 yum 源会失效直接yum install大概率报Could not resolve host: mirrorlist.centos.org。这不是你配置错了是源本身下线了。解决办法是切到 vault 归档源或者国内镜像源具体操作我会在第 3 章里写。3. 方式一用 yum 从仓库在线安装 OpenJDK 113.1 先把软件源修好再谈安装刚才提到的源失效问题是绕不过去的。先看一下当前源配置ls /etc/yum.repos.d/ cat /etc/yum.repos.d/CentOS-Base.repo | head -20如果里面写的是mirrorlist.centos.org那基本可以确定会失败。最省事的做法是换成 vault 归档地址把/etc/yum.repos.d/CentOS-Base.repo里所有mirrorlist开头的行注释掉把#baseurl那一行的注释打开并把域名改成vault.centos.org。整个文件里的$releasever也要确认一下CentOS 7 上这个变量应该解析成7。改完执行yum clean all yum makecache yum repolistrepolist能正常列出仓库列表就说明源修好了。如果嫌手动改麻烦也可以直接下载国内镜像站提供的 CentOS 7 repo 文件覆盖过去速度会快很多尤其是后面装包的时候。注意改 repo 文件时一定要先备份cp CentOS-Base.repo CentOS-Base.repo.bak。改坏了还能回去不然一台机器的包管理就废了。3.2 仓库里到底有没有 OpenJDK 11看清楚再装源修好之后先搜一下别急着 installyum list available | grep -i openjdk yum search java-11正常情况下你会看到几个包它们的职责差别很大选错了会踩坑包名包含内容适用场景java-11-openjdkJRE 部分含完整运行时只跑不编译需要图形/AWT 支持java-11-openjdk-headless精简运行时不含图形库服务器端纯命令行环境体积小java-11-openjdk-devel含javac、jar、jps等开发工具需要编译代码、跑 Maven/Gradlejava-11-openjdk-jmods模块化镜像文件需要定制 JRE 镜像的场景java-11-openjdk-src源码包调试 JDK 本身这里有个非常经典的坑很多人只装了java-11-openjdk或者headless结果发现javac用不了然后开始怀疑人生。原因是javac只存在于-devel包里。服务器上跑 Java 服务我的建议是直接装java-11-openjdk加java-11-openjdk-devel两个-devel会顺带把运行时依赖拉进来省得你纠结选哪个。yum install -y java-11-openjdk java-11-openjdk-devel如果你确定这台机器只部署不编译比如只跑一个 Spring Boot 打好的 fat jar那装java-11-openjdk-headless就够了能少装几十兆的图形相关依赖。装的过程中会看到 yum 自动解决依赖、下载、安装整个过程通常一两分钟。3.3 装完之后JAVA_HOME 到底该指向哪儿装完先别急着配环境变量先看 yum 把东西放哪儿了ls -l /usr/lib/jvm/ rpm -ql java-11-openjdk-devel | grep bin/javac典型的输出是这样的/usr/lib/jvm/java-11-openjdk-11.0.20.1.1.el7_9.x86_64 /usr/lib/jvm/jre-11-openjdk-11.0.20.1.1.el7_9.x86_64注意这里有两个目录一个是完整的 JDK一个是 JRE。JAVA_HOME必须指向完整 JDK 那一个也就是带-11.0.xx且不带jre-前缀的那个。判断方法很直接进去看有没有bin/javac有就是 JDK没有就是 JRE。JDK_PATH$(ls -d /usr/lib/jvm/java-11-openjdk-11* | head -1) echo $JDK_PATH ls $JDK_PATH/bin/javac确认无误后把环境变量写到/etc/profile.d/java11.sh里。之所以推荐放profile.d而不是直接改/etc/profile是因为它按文件加载、互不干扰以后要卸载只要删掉这一个文件就行不用在几百行的 profile 里找自己加的那几行。cat /etc/profile.d/java11.sh EOF export JAVA_HOME/usr/lib/jvm/java-11-openjdk-11.0.20.1.1.el7_9.x86_64 export PATH$JAVA_HOME/bin:$PATH EOF注意上面那个路径要换成你自己机器上ls -d出来的真实结果不要照抄。写完执行source /etc/profile.d/java11.sh或者干脆退出重新登录一次。关于CLASSPATH我现在的习惯是不配。网上很多老教程会让你配CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar但这两个 jar 从 JDK 9 开始就已经不存在了模块化之后换成了jmods目录JDK 11 里根本没有配了不会报错但完全没意义。现代构建工具都是自己管理类路径的手动配 CLASSPATH 反而容易和工具冲突。3.4 alternatives 机制和多版本切换RPM 安装的 Java 会注册到系统级的 alternatives 体系中这是 CentOS 用来管理同一功能多个版本的工具。看当前有哪些 Java 被注册alternatives --display java如果机器上同时有 JDK 8 和 JDK 11想切到 11alternatives --config java alternatives --config javac它会列出一个编号菜单输入对应数字回车即可。切完之后/usr/bin/java的软链接会自动指向新版本。这里要特别泼一盆冷水alternatives 只管/usr/bin/java这个链接它不会自动改你的JAVA_HOME。这是个非常容易被忽略的点。你切了 alternativesjava -version显示 11 了很开心结果一跑 Maven 还是报错说找不到 JDK 11因为 Maven 读的是JAVA_HOME而它还是老值。所以正确做法是两件事都做alternatives 负责命令行java命令的默认版本profile.d里的JAVA_HOME负责给构建工具和服务脚本用。两边保持一致别只改一个。3.5 装完之后跑一遍自检我固定会跑这五条命令做验证java -version javac -version echo $JAVA_HOME which java readlink -f $(which java)预期结果是java -version和javac -version都输出11.0.x且两者小版本号一致JAVA_HOME输出完整路径readlink最终落到你期望的那个 JDK 目录下的bin/java。如果java和javac版本不一致说明你踩到了 PATH 混乱的问题——通常是命令行里残留了另一个 JDK 的 bin 目录用which -a java看看到底有几个。4. 方式二下载 tar.gz 压缩包手动安装4.1 选哪个发行版以及为什么我不建议下完不管校验OpenJDK 是规范具体实现有好几个来源常见的有 Eclipse Temurin原 AdoptOpenJDK 的后继、Amazon Corretto、Azul Zulu、阿里 Dragonwell 等。它们都通过 TCK 认证功能上等价差异主要在长期维护策略、额外的 GC 优化和一些诊断工具。选哪个我的经验是生产环境跟着公司已有的技术栈走没特殊要求就用 Eclipse Temurin 或者 Corretto社区活跃、更新勤、文档全。下载的时候页面一般会给出两个数文件大小和 SHA256 校验值。一定要校验。这不是小题大做压缩包几百兆网络传输中断导致文件不完整的情况真不少见而解压时 tar 不一定会报错可能只是默默解出一堆残缺文件等运行时才报奇怪的ClassNotFoundException排查起来极其痛苦。sha256sum openjdk-11.0.20_linux-x64_bin.tar.gz把输出的哈希和官网给的对比一致再往下走。不一致就重下别抱侥幸心理。4.2 解压目录怎么规划别随手扔在家目录新手最常干的事是把 JDK 解压到~/或者/root/下面然后用 root 的环境变量配好跑得挺爽。问题是换个用户登录就找不到 Java 了或者某天清理家目录顺手删了。我建议统一放/usr/local/下面这是 Linux 里给本地编译安装的软件预留的标准位置语义清晰权限也好管。mkdir -p /usr/local/java tar -zxvf openjdk-11.0.20_linux-x64_bin.tar.gz -C /usr/local/java/ ls -l /usr/local/java/解压出来的目录名可能是jdk-11.0.20。我习惯再建一个不带版本号的软链接这样以后升级 JDK 时只要改软链接指向所有依赖JAVA_HOME/usr/local/java/current的脚本和配置都不用动ln -sfn /usr/local/java/jdk-11.0.20 /usr/local/java/current ls -l /usr/local/java/current这个小技巧看着不起眼但在需要频繁升级小版本的环境里能省掉大量改配置的工作。软链接用的是-sfnn参数很关键——如果目标已经存在且是个指向目录的软链接不加n的话ln会把它当成目录创建出/usr/local/java/current/jdk-11.0.20这种嵌套结构踩过一次就记住了。注意/usr/local/java/和/usr/local/java/current/这两个路径千万不要搞混。JAVA_HOME统一的写法是指向current简单、可升级、不易出错。4.3 用 profile.d 配环境变量比改 profile 干净跟上一种方式一样环境变量还是写到/etc/profile.d/下cat /etc/profile.d/java11.sh EOF export JAVA_HOME/usr/local/java/current export PATH$JAVA_HOME/bin:$PATH EOF chmod 644 /etc/profile.d/java11.shchmod 644这一步别省。有些系统对 profile.d 下的文件权限有要求权限不对可能被跳过不加载表现就是明明文件在那儿echo $JAVA_HOME 还是空的。写完之后新开一个终端窗口验证比source更可靠因为它走的是完整的登录加载流程echo $JAVA_HOME java -version javac -version4.4 手动装的情况下怎么和多版本共存手动安装的 JDK 不会自动注册到 alternatives 体系里所以alternatives --config java里看不到它。如果这台机器上既有 yum 装的 JDK 8又有手动装的 JDK 11想让java命令也走 11有两个做法。做法一是直接用 PATH 顺序压制。因为/etc/profile.d/java11.sh把$JAVA_HOME/bin放在了 PATH 最前面所以登录 shell 里java会优先命中 JDK 11。你可以用which -a java确认顺序which -a java输出里第一行是哪个实际执行的就是哪个。这种做法简单缺点是只对交互式登录 shell 生效systemd 服务或者其他非登录环境可能读不到 profile.d 的内容。做法二是把手动 JDK 也注册进 alternatives让系统级软链接统一管起来alternatives --install /usr/bin/java java /usr/local/java/current/bin/java 1100 alternatives --install /usr/bin/javac javac /usr/local/java/current/bin/javac 1100 alternatives --config java最后那个数字是优先级数字越大越优先。我把 JDK 11 设成 1100比默认的 8 更高所以装完默认就是 11需要切回去的时候再用--config选。至于 systemd 托管的服务最稳妥的做法是在 service 文件里直接写死环境变量别指望它继承登录环境[Service] EnvironmentJAVA_HOME/usr/local/java/current EnvironmentPATH/usr/local/java/current/bin:/usr/bin:/bin这行Environment是我在排查一个手动执行好好的做成服务就找不到 Java的问题时加上的从那之后成了标配。5. 两种方式横向对比与我的选型建议5.1 一张表看清差异对比维度yum 安装tar.gz 手动安装版本控制由仓库决定只能装仓库提供的版本完全自主想要哪个小版本就下哪个安装耗时1-2 分钟自动解决依赖下载 解压3-10 分钟视网速而定磁盘占用约 250-400MB含 devel约 300MB目录路径/usr/lib/jvm/java-11-openjdk-*自定义一般/usr/local/java/升级方式yum update java-11-openjdk下载新包、改软链接、重载环境变量卸载干净度yum remove一步搞定需手动删目录、删 profile.d 脚本、清理软链接多版本共存alternatives 原生支持需手动注册或靠 PATH 顺序依赖完整性RPM 自动校验并补齐需自行确认 glibc 等基础库版本适用场景测试机、CI 节点、内部工具服务器生产环境、需精确版本的场景5.2 我在实际项目里怎么选说点具体的经验。给测试团队开临时环境我一律用 yum因为快而且这些机器生命周期短两三周就销毁了不值得在版本上花心思。给线上服务准备机器我一定用 tar.gz理由有三个一是能锁死小版本避免某次无人值守的yum update把 JDK 从 11.0.20 悄悄升到 11.0.21而新版本恰好改了你依赖的某个行为二是能挂载到统一的软件目录规范下方便做配置管理和批量部署三是想换发行版的时候只改一个软链接运维成本低。还有一种场景是两者混用用 yum 装一个java-11-openjdk-devel满足系统工具的依赖再手动装一个精确版本用于业务靠 PATH 顺序保证业务走到手动版本。这种做法看起来冗余但在系统工具锁死了必须用包管理版本的环境里挺常见只是要记得把 PATH 顺序和 alternatives 都对齐不然很容易出现版本漂移。6. 踩坑记录与常见问题排查6.1 常见问题速查表现象大概率原因处理方式java -version仍显示 1.8PATH 里有旧 JDK 优先命中which -a java查顺序清理旧 exportjavac: command not found只装了 JRE/headless 包补装java-11-openjdk-develMaven 报JAVA_HOME is not defined correctlyJAVA_HOME 指向了/usr/bin/java或 JRE 目录改成 JDK 根目录绝对路径提示UnsupportedClassVersionError编译版本高于运行版本用 JDK 11 重新编译或降级运行 JVM解压报gzip: stdin: unexpected end of file下载文件不完整比对 sha256重新下载cannot execute binary file下载了错误架构的包确认uname -m是 x86_64 还是 aarch64服务启动找不到 Javasystemd 不加载 profile.d在 service 文件里写Environmentyum 报无法解析主机CentOS 7 官方源已下线切换到 vault 归档源或国内镜像源tar解压后目录为空软链接嵌套导致路径错乱用ln -sfn而非ln -sf6.2 我真实踩过的几个坑第一个坑是关于source的迷惑性。我早期调试时习惯用source /etc/profile.d/java11.sh看到echo $JAVA_HOME有输出就以为搞定了结果一开新终端又没了。原因是这个脚本只影响了当前这个 shell 会话我用的还是已经存在的那个终端。正确验证方式永远是关掉窗口重开一个或者用bash -l启动一个新的登录 shell。第二个坑是 JDK 小版本号和目录名不匹配。有一回我照着别人博客里的路径cp了过去忘了改版本号后缀/etc/profile.d/java11.sh里写的路径在机器上根本不存在。诡异的是java -version还能正常输出因为 PATH 里还有另一个 JDK 的 bin 目录兜底。等真正跑构建的时候才炸报的错和 JDK 完全无关查了两小时才对上。从那之后我养成了一个习惯配完环境变量立刻跑ls -l $JAVA_HOME/bin/javac文件不存在就说明路径写错了一秒钟暴露问题。第三个坑和 modules 有关。JDK 11 里有jmods目录里面是.jmod文件不是.jar。我曾经想当然地把jmods/*.jar拼进 CLASSPATH结果自然是找不到文件。JDK 9 之后的模块化体系里rt.jar、tools.jar、dt.jar这些老概念全都消失了涉及的类都在lib/modules这个运行时镜像里。凡是看到教程让你配这三个 jar 的可以直接判断那是 JDK 8 时代的写法照抄到 11 上只会增加困惑。第四个坑是内存。在 VMware 里给虚拟机分了 1GB 内存装完 JDK 跑一个稍大的 Maven 构建直接卡死。用java -XX:PrintFlagsFinal -version | grep -i heapsize看了下MaxHeapSize 只有 256MB 左右因为默认最大堆是物理内存的 1/4。这种时候要么加内存要么显式指定堆参数比如跑构建时加MAVEN_OPTS-Xmx1024m。这是我建议虚拟机至少给 4GB 内存的直接原因。第五个坑关于卸载。手动装的 JDK 卸载不只是删目录。我见过一台机器上残留了三个不同版本的 JDK/usr/local/java/下有四个目录加两个软链接profile.d里两个脚本互相覆盖 PATH。清理顺序应该是先删profile.d里的脚本再删软链接最后删目录删完重开 shell 用which -a java确认干净。6.3 卸载与回滚的完整操作yum 装的卸载非常简单但要注意通配符别写太宽rpm -qa | grep java-11 yum remove -y java-11-openjdk java-11-openjdk-devel先 list 再 remove确认清楚要删哪些。如果你不确定某个包有没有被别的软件依赖可以先用yum remove的 dry-run 模式看它打算删什么yum remove java-11-openjdk --assumeyes --downloadonly手动装的回滚就按刚才说的顺序来rm -f /etc/profile.d/java11.sh rm -f /usr/local/java/current rm -rf /usr/local/java/jdk-11.0.20删软链接的时候用rm -f而不是rm -rf因为后面带斜杠的路径可能会让某些 shell 把它当成目录去递归删虽然current是软链接不会真的删到源目录但养成习惯总没错。最后补一个我常用的检查动作尤其适合在交接别人的机器时跑一遍for f in /etc/profile /etc/profile.d/*.sh ~/.bashrc ~/.bash_profile; do [ -f $f ] grep -Hn JAVA_HOME\|CLASSPATH $f done这条命令会把所有可能影响 Java 环境的配置点全列出来有没有重复定义、有没有互相打架一眼就能看清。我在接手过好几台Java 环境很诡异的机器之后把这个检查固化成了标准流程基本上三分钟之内就能定位到问题源头。
返回列表