☰
Linux ARM 平台安装 JDK 11:aarch64 二进制包部署与避坑指南
2026/10/21 4:58:40 网站建设 项目流程

简介:面向ARM 64位(aarch64)架构Linux平台的JDK 11.0.20安装包,专为需要在国产化服务器、嵌入式设备或树莓派等ARM环境运行Java应用的开发者准备,解决x86版本无法直接使用的问题。压缩包共392个文件,包含71个jmod模块文件、38个so动态库、36个md说明文档、70个版权与许可文件,以及javac、java、jdb、jar等完整命令行工具,整体大小约158MB,目录结构清晰。资源提供了从JRE运行时到编译器、调试器的完整工具链,安装后即可支持Spring Boot、微服务等常见Java应用在ARM设备上的开发与部署;解压后即得标准JDK目录,便于直接集成到构建脚本、容器镜像或CI/CD流水线中。JDK 11作为LTS版本,具备长期安全更新,目前已有697人学习下载,适合需要稳定ARM Java运行环境的中高级开发者和运维人员。

1. 为什么大家都在找 aarch64 的 Linux JDK:不是所有 arm 都能跑同一个包

在 ARM 设备上做 Linux 开发,第一个拦路虎往往不是业务逻辑,而是连 Java 环境都装不顺。apt 源里的 openjdk 版本陈旧,x86 机器上拷来的 JDK 执行时直接甩 cannot execute binary file,这就是 linux arm java 场景最常见的翻车现场。这份 jdk-11.0.20-linux-aarch64-bin 是 JDK 11.0.20 对应 ARM 64 位(aarch64)Linux 平台的官方二进制发行包,在 ARMv8-A 及以上设备解压即用,不用编译源码,也不用改 apt 源。它解决四类诉求:arm64 云主机搭运行环境、鲲鹏/飞腾这类平台适配、树莓派跑 Java 服务、发布镜像时想要一个干净软件包。适合的人群很明确:不想在装环境上反复折腾的一线开发者和运维。

2. 安装前先认清架构:aarch64 和 armv7 的差别决定你下载哪个包

2.1 用 uname -m 确认机器架构,别凭产品名猜

拿到一台 ARM 设备,别急着解压,先回答一个问题:它到底跑的是 64 位系统还是 32 位系统。同样叫 ARM,aarch64 和 armv7 对应的二进制完全不兼容,装错了连错误信息都看不懂。

# 查当前机器硬件架构,这是第一步,也是最容易被跳过的一步 uname -m # 输出 aarch64 => 64 位 ARMv8-A,本文的包适用 # 输出 armv7l => 32 位 ARMv7,需要另找 32 位 arm 包 # 输出 x86_64 => Intel/AMD 平台,别下 aarch64 包 # 想看得再细一点,可以配合这两条 cat /proc/cpuinfo | grep -i "model name\|architecture" | head -n 5 lscpu | grep -E "Architecture|Byte Order"

uname -m 是内核报告的执行环境架构,它反映的是当前操作系统,而不是芯片本身。一个常见场景是:同样的 ARMv8 芯片,如果你给板子刷的是 32 位系统,uname -m 返回 armv7l,此时哪怕 CPU 本身支持 aarch64,强行运行 64 位 JDK 也会失败。反过来,64 位系统上装 32 位 ARM 包,要么提示缺少 32 位运行库,要么程序无法加载。所以最稳的顺序是:先看 uname -m,再决定下载包,最后才解压配置。这一步成本不到 10 秒,能省掉后面一小时的排查时间。

判断完架构,你还需要做一次额外确认:系统里是否已经存在其他 JDK 环境。很多 ARM 设备的出厂镜像预装了 openjdk 8 或 11,如果你只看当前 java -version,很容易误以为环境已经就绪,但那个路径可能挂着一个乱七八糟的半成品。用 which -a java 把所有候选路径列出来,心里有个底再动手,比直接开装要稳。

2.2 二进制 tar 包 vs 源码编译 vs 包管理器,我为什么坚持用官方二进制版

ARM 平台上装 JDK 的路径不止一条,但项目交付和线上环境我更推荐官方二进制 tar 包。先看三种常见获取方式的对比:

获取方式优点缺点
apt install openjdk-11-jdk一条命令装完,自动维护依赖发行版源版本滞后;路径分散在 /usr/lib/jvm;多版本切换麻烦
下载源码自行编译可以定制特性编译一次 20 分钟以上,GCC/autoconf 依赖复杂,aarch64 交叉编译坑更多
tar 官方二进制包与发行版无关,任意目录运行,多版本共存,卸载即删目录需要手动配环境变量,升级需手动处理

为什么强调“官方二进制”而不是“网上随便转存的包”?JDK 发行时会同时给出校验哈希,正规渠道下载后可以核对文件完整性,防止镜像被篡改或传输损坏。另外官方 aarch64 包针对 ARMv8 做了指令集优化,比如对比较并交换指令的利用和内存屏障的调整,这些在源码自编译时未必能正确带出来。顺带说一句,arm32 时代的旧包名里有 arm-vfp-hflt 或 armhf 字样,那是硬浮点 ABI 的标识;64 位现在统一叫 aarch64,认准这个标识就够了。

对于需要反复交付到多台设备的人来说,tar 包还有一个隐藏好处:它不写系统目录、不覆盖已有 openjdk,卸载就是把 /opt/java 下的目录删掉,不会像 DEB 或 RPM 卸载那样遗留一堆配置碎片。团队里如果有人在机器上把原本的 JDK 环境弄坏了,用 tar 包回退比修复包管理器记录简单得多。

2.3 判定包名:从文件名读懂架构、版本与平台

aarch64 的 JDK 包命名有固定规律,拿到压缩包后先对照名称,再决定是否进入安装流程。常见命名格式如下:

包名特征对应平台
jdk-11.0.20_linux-aarch64_bin.tar.gz64 位 ARMv8 Linux
jdk-11.0.20_linux-x64_bin.tar.gzIntel/AMD 64 位 Linux
jdk-11.0.20_linux-arm-vfp-hflt.tar.gz32 位 ARM 硬浮点

实际下载后建议做一次哈希校验。

# Linux 下用 sha256sum 核对发行方公布的校验值 sha256sum jdk-11.0.20_linux-aarch64_bin.tar.gz # 把输出与发行方公布值比对,一致再进入解压步骤 # 校验失败时先重新下载,不要强行解压,否则装出来的 JDK 不可预期

这一步很多人嫌啰嗦,但我在实际交付中遇到过多次压缩包在传输中被截断的情况,哈希不过就硬解,最后编译 Java 程序时出现莫名其妙的类加载异常。文件校验和 uname -m 一样,都是十秒内能做掉的动作,属于运维习惯问题。解压前再顺手看一眼压缩包内目录结构,确认没有多套嵌套目录,也能避开后面路径写错的坑。

3. 把 JDK 11 装进 Linux-Arm 环境:解压、环境变量与 systemd 调用

3.1 目录规划:按版本号落位,别把解压目录改名成 jdk

解压本身没难度,但目录规划决定了后续三个月升级、回滚时的心情。我见过不少人把压缩包解压后顺手 mv 成 jdk,等到装 11.0.21 时就傻眼了:新老版本混在一起,根本分不清线上用的是哪套。规范的落位方式是这样:

# 建立统一的软件根目录 sudo mkdir -p /opt/java # 解压到 /opt/java,tar 包自带版本号目录,不额外改名 sudo tar -xzf jdk-11.0.20_linux-aarch64_bin.tar.gz -C /opt/java/ # 确认顶层结构是否符合预期 ls -lh /opt/java/jdk-11.0.20/

解压之后的顶层目录里应该有 bin、lib、conf、include 等子目录,bin/java 就是入口。注意这一步不要用其他解压工具直接抓文件,tar 在 ARM 平台和 x86 平台都能正常解压,但前提是文件权限和软链接要保留。tar 解压会保留包内的符号链接属性,这对后续运行很关键,具体原因在第 4 章的排查清单里展开。

3.2 配置 JAVA_HOME 和 PATH:写到 /etc/profile.d,别改用户 bashrc

很多教程让你往 ~/.bashrc 里写 export JAVA_HOME,这在单用户开发机上凑合能用,但到了多用户、多服务的环境里就会有各种漏配。我习惯写一个系统级脚本:

# 创建 /etc/profile.d/jdk11.sh,内容两行 export JAVA_HOME=/opt/java/jdk-11.0.20 export PATH=$JAVA_HOME/bin:$PATH

文件建好后,新开一个 SSH 会话,登录 shell 会通过 /etc/profile 自动加载 profile.d 下的所有脚本,root 和普通用户都生效。关键是 PATH 的顺序:$JAVA_HOME/bin 放在前面,就会优先找到新装的 java,而不是 /usr/bin/java。配置完不用重启机器,执行 source /etc/profile 或在当前会话重开一个登录窗口即可。

# 验证环境变量是否按要求生效 echo $JAVA_HOME # 期望输出 /opt/java/jdk-11.0.20 which java # 期望输出 /opt/java/jdk-11.0.20/bin/java java -version # 期望显示 11.0.20 和 aarch64 相关字样

这里补一个容易踩的细节:不要用 bash 的 hash 缓存来判断命令路径。如果你之前在某个会话里执行过 java,当前 shell 会记住旧路径,重开终端窗口或用 exec bash 刷新当前会话再验证。hash -r 清空路径缓存也可以,但新窗口最干净。

3.3 systemd 服务里调用 JDK:写绝对路径,别依赖 PATH

Java 服务常以 systemd 托管方式运行,而 systemd 的 service 环境变量和我们 SSH 登录拿到的环境变量不是同一套。profile.d 只在登录 shell 加载,systemd 启动服务时不会读取它,于是在命令行里 java 跑得好好的服务,一 service 化就报 command not found。这不是 JDK 的问题,是环境边界问题。

[Unit] Description=某 Java 11 服务 [Service] WorkingDirectory=/opt/app/demo # 用完整路径指向刚装的 JDK,避免依赖 PATH ExecStart=/opt/java/jdk-11.0.20/bin/java -Xms512m -Xmx2g -jar demo-1.0.0.jar --server.port=8080 Restart=on-failure User=appuser [Install] WantedBy=multi-user.target

如果你希望 service 里也直接用 $JAVA_HOME,可以在 [Service] 段加一行 Environment=JAVA_HOME=/opt/java/jdk-11.0.20,然后在 ExecStart 里写 $JAVA_HOME/bin/java。注意 systemd 的 ExecStart 默认不经过 shell,直接写 $JAVA_HOME 不会做变量展开,必须配合 Environment= 才能识别。对 aarch64 平台来说,JVM 的 -Xms、-Xmx 参数语义和 x86 平台没有差异,内存分配策略是一样的,只是底层指令不同,不用做特殊调整。

3.4 已经装了旧 JDK 怎么办:检查优先级,而不是直接卸载

arm 设备上很多基础系统默认预装了 openjdk-8 或 openjdk-11,在不确定哪个服务依赖它之前,不要直接卸载。先看当前环境中哪个 java 生效:

which -a java 2>/dev/null # 会列出 PATH 中所有名为 java 的可执行文件,按优先级排序 # 第一个最优先,通常 /usr/bin/java 是 alternatives 的符号链接 sudo update-alternatives --list java 2>/dev/null # 查看系统管理的 Java 候选列表

推荐的做法是把新 JDK 的 bin 目录放在 PATH 最前面,不卸载旧版本。这样旧服务的启动脚本如果写死了 /usr/bin/java,不会受到任何影响;新部署的服务则优先使用 11.0.20。等观察一段时间,确认没有服务依赖旧版本,再清理也不迟。这种并行策略在 arm 服务器上尤其实用,因为旧 JDK 的依赖库经常被其他组件悄悄复用,强行卸载容易把系统组件拖下水。

4. 常见问题排查:aarch64 平台装 JDK 的四类翻车现场

4.1 现象:java -version 提示 cannot execute binary file

arm 机器上解压完 aarch64 包,一执行就报 cannot execute binary file,或者提示 Exec format error。这种报错一看是格式问题,但很多人不知道格式是跟系统跑的而不是跟 CPU 跑的。原因通常是两种:一是系统其实是 32 位 ARM,uname -m 返回 armv7l,而包是 64 位;二是系统是 x86 平台,却下载成了 aarch64 包。解决方法是先跑 uname -m 确认,再用 file 命令查看二进制格式:

uname -m # 期望 aarch64 file /opt/java/jdk-11.0.20/bin/java # 期望输出 ELF 64-bit LSB executable, ARM aarch64

如果 file 显示的是 ARM aarch64,但 uname -m 是 x86_64,说明包下载错了,去换 x64 版本。如果 uname -m 是 armv7l,那就老老实实找 32 位 ARM 包。不要试图在 32 位系统里强行运行 64 位 JDK,这一步没有后悔药。

4.2 现象:配置完 JAVA_HOME,java -version 还是旧版本

明明 /etc/profile.d/jdk11.sh 写好了,source 也执行了,java -version 打出来的还是老的 1.8。这种场景在 arm 服务器上很常见,因为几个包管理器预装的 JDK 路径会被其他脚本提前注入。原因是 PATH 变量里 /usr/bin 在 $JAVA_HOME/bin 前面,或者系统用 update-alternatives 强行设置了默认 java。解决分两步:先 which -a java 看有没有两套 JDK 并存,再检查 jdk11.sh 里的 PATH 顺序。把 $JAVA_HOME/bin 放在最前面重新登录,问题通常就消失。如果机器里安装了某个发行版自带的 java 包装脚本,它在 /usr/bin/java 做了优先级劫持,用 which -a 看不到,直接 ls -l /usr/bin/java 检查是不是 alternatives 链接。

which -a java ls -l /usr/bin/java # 输出类似 /usr/bin/java -> /etc/alternatives/java # 此时可以用 sudo update-alternatives --set java /opt/java/jdk-11.0.20/bin/java

这里有个细节:update-alternatives --set 只是改了当前会话的默认值,重启后可能被发行版脚本重置,所以 profile.d 的 PATH 前置仍是最终保障。

4.3 现象:把目录整体拷贝到另一台机器,运行报 No such file or directory

用 cp -r 把 JDK 目录从机器 A 复制到机器 B,结果执行 java 报 No such file or directory,文件明明就在那里。常见原因有两个:一个是包内的 bin/java 是软链接,指向 lib 目录下的真实可执行文件,cp -r 在跨文件系统复制时不保留链接或改变了软链接相对路径;另一个是平台不匹配,复制时没留意目标机器架构。排查方法:

ls -l /opt/java/jdk-11.0.20/bin/java # 如果是 lrwxrwxrwx,注意软链接的目标是否存在 # 重新打包传输,推荐用 tar 而不是 cp tar -czf jdk11-aarch64.tar.gz /opt/java/jdk-11.0.20 # 再在目标机解压,链接关系保持不变

在本地构建机之间同步 JDK,我一般习惯用 tar 归档传输而不是 cp -r,既能保留链接,也能保留执行权限。实在要用 cp,记得加 cp -a 保留全部属性。

4.4 现象:极简系统上运行提示缺少 GLIBC 版本

开发好的剪裁版 Linux 或基础镜像,把 JDK 放进去后启动报 GLIBC_2.27 not found。这是因为官方二进制包在构建时链接了新版本的 glibc,而精简系统的 C 库较老。解决思路不是去镜像里手动拷库文件,那样容易造成库版本冲突,让整个系统变得不可预期。正确做法是选用带完整 glibc 的基础镜像,或者在构建镜像时用 jlink 生成定制运行时,把不需要的模块裁剪掉,同时配合目标镜像的 glibc 版本做验证。检查 glibc 版本可以用 ldd --version。在 arm 平台上,jlink 命令本身和 x86 一致,只是生成的目录是 aarch64 原生二进制。这条在生产环境比较关键,因为很多嵌入式网关盒子就是精简系统,没有图形界面也没有多余运行库。

4.5 现象:装完不知道验证什么,跑个 Hello World 才算真装通

前面几类问题解决后,用一个标准 Java 程序做冒烟测试,确认编译器、运行时、类路径都正常。

cat > /tmp/Hello.java <<'EOF' public class Hello { public static void main(String[] args) { System.out.println("arch=" + System.getProperty("os.arch")); System.out.println("java=" + System.getProperty("java.version")); } } EOF javac /tmp/Hello.java java -cp /tmp Hello # 期望输出两行: # arch=aarch64 # java=11.0.20

如果输出 arch=x86_64,说明 PATH 里跑的还是 x64 JDK;如果编译正常但运行报错,则回到 4.1 节查架构。Hello World 在这里不是教学示例,而是验证工具链的最小闭环,占了很小的时间成本,但能拦住绝大多数环境问题。

5. 多版本切换与批量部署:让 JDK 11 在 arm 设备上不再玄学

5.1 用软链接管理当前版本

把版本号写进目录的目的就在这件事:用 /opt/java/current 作为浮动指针。

sudo ln -sfn /opt/java/jdk-11.0.20 /opt/java/current

profile.d 里的 JAVA_HOME 改为 /opt/java/current,后续所有服务都通过 current 引用。升级时解压新版本号目录,更新链接,重启服务即切换;要回滚,再 ln -sfn 指回老版本即可。ln -sfn 的 n 参数在软链接场景不可省,否则在已存在目录链接时会做出错误行为。这一步做完,版本切换就从一个“装包、改配置、重启机器”的重操作变成了一条命令的事。

5.2 批量部署的最小打包脚本

本地验证通过后,如果要把这套环境推到同架构的多台机器,可以打一个环境包:

# 打包 JDK 和全局配置,注意用 tar 保留链接属性 sudo tar -czf jdk11-aarch64-env.tar.gz \ /opt/java/jdk-11.0.20 \ /etc/profile.d/jdk11.sh # 目标机器上执行 sudo tar -xzf jdk11-aarch64-env.tar.gz -C /

这样做保证每一台机器的 JDK 内容一致,不经过 apt 仓库,不受个别机器源配置影响。同架构批量机器验收时,写一个极简检查脚本:

#!/bin/bash if [ "$(uname -m)" != "aarch64" ]; then echo "架构不匹配,期望 aarch64" exit 1 fi JAVA_BIN=$(command -v java) echo "java 路径: $JAVA_BIN" $JAVA_BIN -version 2>&1 | head -n 1

脚本第一行就拦截架构错误,避免把包推到不符合条件的机器上浪费时间。实际操作中还可以把 java -version、javac -version、file $JAVA_BIN 三行写进同一个脚本,推动化验收流程,输出统一格式,方便批量核对。

我头一回在 arm 设备上做这类部署时,就是少了 uname -m 这一步,把 x64 环境推到目标板,等报 Exec format error 才意识到问题,前后多折腾了大半天。从那以后我每次装机前都强制走一遍“uname -m、java -version、file $(which java)”三连确认;服务切换版本后再花十秒检查 /opt/java/current 的指向。这套习惯在 arm 平台尤其值得坚持,因为二进制格式不做任何提示性友好报错,一次疏忽就是一次返工。希望这些细节帮你在自己的 arm 设备上少踩几个坑。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询