ARM版OpenJDK 11部署全攻略:从下载到Spring Boot实战
简介面向ARM架构Linux平台的OpenJDK 11更新版JDK压缩包专为中标麒麟、银河麒麟等国产操作系统优化适合在ARM服务器、嵌入式设备及信创环境中开发、运行Java应用的开发者与运维人员。压缩包共492个文件、约173.88MB其中bin目录集中了java、javac等常用命令lib目录包含运行库与jmod模块文件另有so动态库、license许可、man手册等类型目录划分清晰便于按需调用。当前已有3238人学习下载。该版本基于OpenJDK 11.0.810构建内置针对ARM处理器优化过的HotSpot虚拟机与JRE运行环境并附带JNI头文件、安全策略和合法授权信息解压后即可作为完整JDK开发环境使用。对需要把Java业务系统迁移到国产化平台的技术团队来说这份包能省去手动编译OpenJDK的繁琐过程直接支持应用调试、性能调优与交付部署有利于Java生态在自主可控环境中的落地实用价值明显。 拿到这个 ARM版的OpenJDK11U-jdk_arm_linux_hotspot_11.0.8_10.tar.gz 文件很多在 ARM 设备上折腾过 Java 的同学应该不陌生。之前我在给一台基于 ARM 架构的 Linux 小主机部署 Spring Boot 服务时就因为这个包的版本和架构折腾了一整天最后才发现问题出在没搞清arm到底指的是 32 位还是 64 位。这个文件名信息量很大它记录了 OpenJDK 版本是 11.0.810虚拟机实现是 HotSpot目标平台是 ARM Linux同时也暗示了这是一套用 tar.gz 打包的便携版环境。这篇文章我会从实际部署的角度出发把这个 ARM 版 OpenJDK 包从下载、校验、安装到配置、排错、部署应用的完整链路讲清楚。不管你是刚拿到树莓派、RK3588 开发板还是要在国产化 ARM 桌面机上跑 Java 应用照着这套流程走基本不会掉进坑里。1. 认识这个安装包ARM Linux 下的 OpenJDK 111.1 OpenJDK 11 和 HotSpot 虚拟机先把这个文件名拆开看。OpenJDK11U-jdk表示这是 OpenJDK 11 的更新版本Updatearm_linux说明目标系统是 ARM 架构下的 Linuxhotspot是 JVM 的实现方式11.0.8_10对应 Java 版本 11.0.8build 号为 10。这类包通常来自 Adoptium 社区原 AdoptOpenJDK后来改名叫 Temurin是目前大家用得最广的 OpenJDK 发行渠道。HotSpot 是 Oracle 开源出来的高性能虚拟机也是 OpenJDK 默认使用的 JVM 实现。它和其他 JVM 实现比如 OpenJ9相比最大的特点是内存占用相对高一点但启动稳定、调优参数丰富、生态资料多。在 ARM 设备上跑 Java 应用我个人会优先选择 HotSpot因为网上能查到的 JVM 调优经验基本都是针对 HotSpot 的遇到问题好排查。OpenJDK 11 是 LTS长期支持版本这意味着安全更新维护周期很长非常适合生产环境。对很多团队来说从 JDK 8 升到 JDK 11 是一个不大不小的跳跃一方面模块化系统JPMS已经在实际生效另一方面一些老库在 Java 11 下可能有点兼容性问题。但相比 JDK 17 的更多新语法11 的保守程度刚好适合大多数企业级应用。如果你的应用就是 Spring Boot 2.x 或常规 Web 服务OpenJDK 11 是一个很稳的选择。1.2 为什么选 tar.gz 而不是 apt 或 rpm不少人在 ARM Linux 上装 Java 第一反应是用包管理器在 Debian 系上执行apt install openjdk-11-jdk在 Red Hat 系上执行yum install java-11-openjdk。这个思路没问题但在实际用的时候我经常被坑到主要原因有三个apt 源里的 JDK 版本不一定受你控制。比如某些定制 ARM Linux 发行版的软件源没有同步最新 OpenJDK 11装出来可能是 11.0.2 甚至更老的 11.0.1 版本存在已知安全漏洞。包管理器会把 JDK 的目录拆得七零八落。Java 主程序在/usr/lib/jvm/java-11-openjdk-arm64/配置文件在/etc/java-11-openjdk/头文件在别的目录。手动管理很难受。tar.gz 是真正的绿色版。解压到任意目录即可用不会污染系统也不需要 root 权限如果放到用户目录的话。对于嵌入式设备、交叉开发环境、CI 打包机来说这种便携性非常宝贵。我整理了一个简单对比方便你决定用哪种方式安装方式优点缺点tar.gz 解压目录独立、版本可控、便于多版本共存没有自动更新需要自己管理apt / yum安装简单、自动处理依赖和环境变量版本滞后、目录分散、定制系统源可能缺失rpm 包适合 Red Hat 系系统管理ARM 系统需要额外找 rpm 仓库配置麻烦在很多嵌入式项目里更关注的是“我要一个精确版本的 JDK 放在指定目录系统上不要有任何多余的包”这种情况下 tar.gz 明显是更合适的选择。2. 部署前的准备工作2.1 确认 CPU 架构和系统位数这是最容易翻车的一步。标题里写的是arm_linux但这个表述其实不够精确。ARM 平台上常见的架构有armv7l32 位 ARM和aarch64ARM 64 位这两种架构的安装包是不通用的。像树莓派 2 的是 armv7l树莓派 4 是 aarch64RK3588 开发板通常是 aarch64。下载安装包之前一定要先确认系统的实际架构。用下面这三条命令可以快速判断# 查看内核架构最直观 uname -m # 如果你用的是 Debian 系的系统可以看 dpkg 的架构 dpkg --print-architecture # 更彻底一点直接看系统里二进制文件的格式 file /bin/ls正常情况下uname -m的输出如果是aarch64你就应该下载文件名里包含aarch64的 JDK 包如果是armv7l则要找arm或者armhf版本。不过要注意网上流传的不少 ARM 版 OpenJDK 文件名里直接写arm_linux实际指向的是 32 位 ARM 版本。我在树莓派 3 上就遇到过下载了文件名带arm_linux的包硬是在但系统跑了会儿就报段错误最后才发现那是 armv7l 的版本不是我需要的。所以千万别只看文件名一定要以uname -m的实际结果为准。另外还要确认系统位数。现在的 ARM Linux 系统大多都是 64 位的但也有一批老设备跑的是 32 位内核。如果你不确定就执行getconf LONG_BIT返回64就是 64 位系统返回32就是 32 位系统。JDK 的版本选择必须和这两者完全匹配否则再怎么说都会在运行时报错。2.2 下载与校验安装包确认架构之后下一步就是找到正确的下载地址。Adoptium 官方提供 API 下载但很多时候我们拿到的包是从镜像站或者同事手里拷过来的。假设你拿到的就是标题里的这个包我建议从官方 GitHub Release 下载对应版本。对于 11.0.810 这个版本下载地址类似wget https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.8%2B10/OpenJDK11U-jdk_aarch64_linux_hotspot_11.0.8_10.tar.gz注意这里 URL 中的%2B是号的 URL 编码如果直接用会导致下载失败。如果你是 32 位 ARM那么文件名可能是OpenJDK11U-jdk_arm_linux_hotspot_11.0.8_10.tar.gz这个得看具体发布页面。下载完成后强烈建议先做校验。官方通常会在发布页面提供对应的 SHA256 校验和文件你可以用sha256sum来验证下载的包是否完整防止传输损坏或被人篡改# 先计算本地文件的 SHA256 值 sha256sum OpenJDK11U-jdk_aarch64_linux_hotspot_11.0.8_10.tar.gz # 再将输出的哈希值和官方提供的值比对 # 也可以将官方校验值写入文件然后执行自动校验 echo 官方SHA256值 OpenJDK11U-jdk_aarch64_linux_hotspot_11.0.8_10.tar.gz sha256.txt sha256sum -c sha256.txt如果输出是OK说明文件没问题。这一步看似多此一举但在嵌入式场景中下载过程经常断点续传导致文件不完整我曾经因为没校验解压后java -version报错 “Invalid or corrupt jarfile”源头就是 tar.gz 下载多了几个字节。所以别省这一下。3. 安装与配置实操3.1 解压并移动到指定目录校验通过之后就可以正式安装了。我一般会把 JDK 放在/opt下因为/opt本来就是被设计用来存放第三方独立软件包的目录位置统一、权限清晰。如果你只想装在用户目录下也可以放到~/jdk但要注意登录 Shell 的环境变量作用域不同。操作命令如下# 1. 解压到当前目录 tar xzf OpenJDK11U-jdk_aarch64_linux_hotspot_11.0.8_10.tar.gz # 2. 查看解压出来的目录名一般是 jdk-11.0.810 ls -d jdk-* # 3. 创建 /opt/jdk 目录并把解压后的目录移动过去 sudo mkdir -p /opt/jdk sudo mv jdk-11.0.810 /opt/jdk/ # 4. 为了后续维护方便设置一个无版本号的软链接 sudo ln -snf /opt/jdk/jdk-11.0.810 /opt/jdk/current这里有一个细节不要直接把整个解压目录留在root下或者使用版本号目录操作。用软链接current指向当前版本以后如果要升级 JDK只需要解压新版再改一下软链接指向即可服务无需修改任何配置就能切换到新版本。这是我在多次升级 JDK 后总结出的经验省事很多。另外解压后还要确认一下关键目录的内容是否正常ls /opt/jdk/jdk-11.0.810/bin/你会看到java、javac、jlink、jhsdb等可执行文件。如果bin目录不存在说明你下载的可能是 JRE 而不是 JDK开发编译环境就装不了。如果你是在非 root 用户下运行服务还要注意/opt/jdk下所有文件的权限。有些系统会把/opt下的目录默认设为 root:root普通用户无法读写。如果后续服务用户是app最好授权一下sudo chown -R app:app /opt/jdk/jdk-11.0.810或者保持 root 所有但确保普通有执行权限bin下的执行文件默认是 755没问题但要保证用户能读lib目录。大多数情况下 tar 包解压出来的权限都是正确的如果出现 Permission denied 再检查这里。3.2 配置 JAVA_HOME 和 PATH环境变量是我见过最多人配置错的地方。很多教程直接让你改/etc/profile但这样会污染全局登录脚本不够干净。更优雅的做法是在/etc/profile.d/下新建一个独立脚本系统登录时会自动加载sudo tee /etc/profile.d/java.sh EOF export JAVA_HOME/opt/jdk/current export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar EOF这里CLASSPATH其实在 Java 11 里已经不需要手动设置了因为 JDK 的模块化机制会自己处理类路径。但我见过一些老项目还依赖CLASSPATH环境变量所以保留这一行也无妨只是要清楚它不是必需的。脚本写好后让当前 Shell 重新加载source /etc/profile.d/java.sh如果你只是想在当前终端临时用一下直接执行 export 也可以但重启后就会失效。配置在/etc/profile.d/java.sh的好处是所有用户登录时都会自动生效这对多用户服务环境非常友好。如果你所在的系统没有/etc/profile.d目录比如某些精简嵌入式系统那就只能退而求其次在/etc/profile末尾追加同样的内容或者对特定用户在~/.bashrc里配置。3.3 验证安装结果配置完成后不要急着跑业务先做几个基础验证# 查看 java 版本重点是确认是 11.0.8且是 HotSpot 实现 java -version # 查看 javac 版本确认编译工具可用 javac -version # 确认 JAVA_HOME 是否正确 echo $JAVA_HOME # 确认 java 实际指向的路径 readlink -f $(which java)正常输出应该类似openjdk version 11.0.8 2020-07-14 OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.810) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.810, mixed mode)让我特别提醒如果java -version显示的是其他版本比如 1.8.0说明你系统里已经存在其他 JDK 并且 PATH 优先级比这个新配置高。这时候按下面的排查思路走基本上都是 PATH 顺序问题。4. 常见问题与排错实录4.1 “cannot execute binary file” 架构不匹配这是我见过最多的情况。当你执行java -version终端直接返回bash: /usr/bin/java: cannot execute binary file: Exec format error这基本可以肯定是架构不匹配。比如你下载的是 aarch64 版本但系统是 armv7l或者反过来。解决办法很简单重新确认uname -m下载对应架构的安装包。除了下载新包还可以用file命令直接看安装包里的主要可执行文件是什么架构# 解压后执行 file /opt/jdk/current/bin/java输出里会明确标注ELF 64-bit LSB shared object, ARM aarch64或ELF 32-bit LSB ... ARM。如果和系统架构不一致那就别浪费时间直接换包。还有一种类似报错是Permission denied这是因为没有执行权限。tar 包解压一般不会丢权限但如果把文件从 Windows 共享里拷过来可能会把执行权限弄丢。解决办法chmod x /opt/jdk/current/bin/java /opt/jdk/current/bin/javac4.2 版本号显示不对或环境变量未生效java -version能运行但版本是老的或者干脆提示command not found。这种情况有两种可能。第一系统里已经有一个 JDK 被包管理器注册到/usr/bin/java而PATH里/usr/bin排在/opt/jdk/current/bin的前面。执行which java看看路径就能发现问题。如果确实是因为顺序问题最简单的办法是用update-alternatives来管理Debian 系系统适用sudo update-alternatives --install /usr/bin/java java /opt/jdk/current/bin/java 100 sudo update-alternatives --config java这样系统会优先使用你指定的 JDK。javac也可以用同样的方式配置。第二你配置的/etc/profile.d/java.sh没生效。比如当前 Shell 是 zsh或者用su切换到另一个用户时source没有被执行。这种情况下最直接的验证方式是echo $PATH检查里面有没有/opt/jdk/current/bin。没有的话手动执行source /etc/profile.d/java.sh再试。如果嫌麻烦退出当前终端重新登录通常就能解决。4.3 定制 ARM Linux 系统下的特殊注意点现在很多 ARM 设备用的是精简过的定制 Linux比如一些盒子、工控机系统它们的 glibc 版本可能很老导致 OpenJDK 11 跑不起来报错类似/lib/arm-linux-gnueabihf/libm.so.6: version GLIBC_2.27 not found。遇到这种情况说到底就是系统基础库版本太低OpenJDK 11 的官方二进制包要求一定版本的 glibc。解决办法有几条路可以走一是换更早的 OpenJDK 版本比如 OpenJDK 8它对 glibc 的要求低很多二是看系统有没有提供软件源里的新版 glibc风险较大不建议强行升级三是如果只是内部工具可以考虑用 musl 构建的 JDK不过部署复杂度会上升。我自己在工控机上碰到过这类问题最后选了 OpenJDK 8 才稳住。如果你做新项目尽量选系统资历较新的 ARM 设备能省很多事。另外有些嵌入式系统用的是 busybox 而不是完整的 coreutilstar、sha256sum命令可能不完全支持。比如sha256sum可能不存在这时候可以用openssl替代openssl dgst -sha256 OpenJDK11U-jdk_aarch64_linux_hotspot_11.0.8_10.tar.gz我在精简系统上经常用这个方式校验文件还是那句老话多一个备用手段现场排障就从容很多。5. 在 ARM 设备上让 Java 应用跑起来5.1 用 JAR 包启动一个 Spring Boot 服务部署好了 JDK总得实际用起来。假设你有一个 Spring Boot 应用的 JAR 包比如app.jar在 ARM 设备上启动其实和 x86 服务器上没有太大区别重点在于内存管理。很多 ARM 小主机的内存只有 1GB 或 2GB如果直接按照默认 JVM 参数跑很可能会发现内存一下子被吃光系统卡死。我一般会这样启动nohup java -Xms128m -Xmx256m -XX:UseG1GC -jar app.jar --server.port8080 app.log 21 这里的-Xms和-Xmx要老老实实根据设备内存配置。实测下来一个不复杂的 Spring Boot 服务256m的堆内存基本够用。如果内存实在吃紧可以把-Xmx降到128m但要注意业务高峰期 GC 会比较频繁延迟会变高。另外为了不让日志文件无限膨胀建议加上-Xlog:gc*:filegc.log记录 GC 日志方便后面排查问题java -Xms128m -Xmx256m -XX:UseG1GC -Xlog:gc*:filegc.log:time,uptime,level -jar app.jar 5.2 配置 systemd 守护进程仅仅用nohup启动服务进程挂了不会自动拉起。在 ARM 设备上我希望服务能开机自启、崩溃自动重启。这里推荐用 systemd 来管理。创建一个服务文件sudo tee /etc/systemd/system/myapp.service EOF [Unit] DescriptionMy Java App Afternetwork.target [Service] Typesimple Userapp WorkingDirectory/opt/myapp ExecStart/opt/jdk/current/bin/java -Xms128m -Xmx256m -jar /opt/myapp/app.jar Restarton-failure RestartSec10 [Install] WantedBymulti-user.target EOF然后执行sudo systemctl daemon-reload sudo systemctl enable myapp sudo systemctl start myapp sudo systemctl status myapp这里的Restarton-failure很重要意味着服务异常退出后 10 秒会自动拉起。在掉电频繁的嵌入式环境里这个配置比什么都可靠。另外给服务单独建一个用户比如app避免用 root 跑 Java 服务也是安全习惯。如果设备太老没有 systemd只有 SysVinit 或者启动脚本那你需要写一个/etc/init.d/myapp脚本核心逻辑差不多无非是 start/stop 时用start-stop-daemon包一层。不过新一点的 ARM Linux 发行版基本都支持 systemd这个方案足够通用。5.3 JVM 参数调优与交叉部署经验在 ARM 设备上跑 JVM除了堆内存还有几个参数值得注意参数作用我的建议-XX:UseContainerSupport让 JVM 感知容器内存限制在 Docker 里跑任务时开启-XX:MaxRAMPercentage50让 JVM 使用容器内存的 50% 作为最大堆配合容器使用很舒服-XX:UseG1GC使用 G1 垃圾收集器多核设备推荐单核小内存可换成 SerialGC-XX:ExitOnOutOfMemoryError堆溢出时直接退出进程配合 systemd 自动重启很合适特别提醒如果你是在 Docker 里部署一定要加-XX:UseContainerSupport和-XX:MaxRAMPercentage这两个参数否则 JVM 会认为你的容器能用的内存等于宿主机物理内存导致内存配置不准确直接超限。还有个场景叫“交叉部署”你在 x86 的编译机上把 Java 应用打成 JAR 包然后传到 ARM 设备上运行。这本来不需要交叉编译因为 JAR 包是平台无关的只要目标 ARM 设备有对应的 JVM 就行。但如果你想在 ARM 设备上动态编译本地代码JNI那就必须用 ARM 版的 JDK也就是本文一直在说的这个安装包。如果你想要一个更小的运行时比如只有 60MB 而不是 300MB可以用 OpenJDK 自带的jlink工具裁剪# 在已经配置好 JDK 的环境下执行 jlink --module-path $JAVA_HOME/jmods --add-modules java.base,java.sql,java.desktop,java.naming --output /opt/mini-jdk这样得到的/opt/mini-jdk就是一套精简版 JDK体积小很多很适合塞进磁盘空间有限的嵌入式 ARM 设备里。不过要注意使用jlink之后javac等开发工具可能不会被保留需要额外确认业务代码是否用到反射或者动态代理如果用了最好还是使用完整版 JDK 或者把对应的模块一起加进去。还有一个比较实用的习惯是在 ARM 设备上跑 Java 应用建议先用top或htop观察一下实际内存占用再决定 JVM 参数怎么调。我见过不少人在 2GB 内存的开发板上直接跑默认参数的 Spring Boot结果 JVM 贪心地占用了 1GB 多系统几乎卡死。后来把堆内存调低给系统留足余量整个设备立刻就流畅了。ARM 设备资源本来就紧JVM 参数一定要量力而行。最后说说版本管理的体会。多版本 JDK 在同一台 ARM 机器上共存时我习惯把下载的 tar 包都放在/opt/jdk/目录下通过软链接current切换当前版本。这样部署和升级都清晰不会出现“我不知道当前用的是哪个 JDK”的尴尬情况。如果你也经常要在 ARM 设备之间切换环境建议把 JDK 目录打个 tar 包备份复制到新设备后解压就能用效率很高。这个 ARM 版的 OpenJDK 11 包我前前后后部署过不下十次。每次踩坑之后都发现问题往往不是出在包本身而是架构确认、环境变量和资源约束这几件事上。把前面这些步骤都养成习惯你在 ARM Linux 上跑 Java 应用的速度会快很多。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →