ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ARM Linux上部署OpenJDK 11:从解压到性能调优的完整避坑指南

ARM Linux上部署OpenJDK 11:从解压到性能调优的完整避坑指南 简介ARM版OpenJDK 11.0.810 HotSpot开发工具包专为ARM架构Linux系统构建并针对中标麒麟、银河麒麟等国产操作系统完成适配可为信创环境、嵌入式设备或ARM服务器上的Java开发与部署提供开箱即用的运行环境。压缩包共包含492个文件大小约173.88MB内部按JDK标准目录组织主要提供java、javac、jshell、jar、jlink等命令行工具jmod模块描述文件so动态链接库以及安全证书、许可文件等配置。可执行文件、运行库与模块文件分层清晰解压后配置JAVA_HOME和PATH即可使用适合离线安装与批量部署。该版本内置专门针对ARM平台优化的HotSpot虚拟机能有效利用处理器特性提升运行效率同时完整保留JDK各组成部件便于开发者排查兼容性问题、构建容器镜像或研究OpenJDK模块化结构。已有3238人浏览学习适合信创项目开发者、嵌入式Linux工程师以及对Java底层实现感兴趣的读者学习研究。 说起 ARM 环境的 Java 运行时不少搞嵌入式、国产化服务器或者树莓派这类设备的同学应该都被这个文件名折腾过OpenJDK11U-jdk_arm_linux_hotspot_11.0.8_10.tar.gz光看这一长串名字很多人第一反应是“这不就是个 JDK 压缩包吗解压就能用”。但真正动手装过之后你会发现里面全是门道——为什么有的机器装不上为什么解压完 java -version 还是报错为什么明明有 arm 版跑起来性能却不尽如人意这篇文章我从头到尾拆一遍这个包把我实际部署过程中踩过的坑、验证过的方案、排查问题的思路全部写出来希望能帮你少走点弯路。这篇内容适合这几类人看刚接触 ARM 平台开发、需要在树莓派/NXP/瑞芯微等 ARM 设备上跑 Java 服务的开发者在做国产化适配、信创项目落地的后端工程师以及买了 ARM 云服务器后不知道怎么配 Java 环境的运维同学。已经熟练在 x86 上搞 JDK 的老手也可以扫一眼选型部分看看 Hotspot 和 OpenJ9 的差异你之前是不是忽略了。1. 文件名逐段解构看懂这个包到底装的是什么1.1 arm_linux 和 hotspot 各代表什么先看最容易被忽略的前半段OpenJDK11U-jdk_arm_linux_hotspot。这个命名规则来自 AdoptOpenJDK 项目后来改名成 Adoptium。其中11U代表 Java 11 的 Update 版本arm_linux表示目标平台是 ARM 架构 Linux 系统hotspot说明用的是 Oracle 开源的 HotSpot 虚拟机。这里有个知识点必须说清楚文件名里的arm_linux是个统称但 ARM 平台其实分两种——AArch6464 位 ARMv8比如鲲鹏 920、飞腾 FT-2000、树莓派 4 的 64 位系统和 ARMv7 hard-float32 位比如树莓派 2/3 的 32 位系统、很多工业级 ARM 板子。arm_linux_hotspot这个命名通常指的是 32 位 ARMv7 版本而 64 位 ARM 的包名一般是aarch64_linux。所以下载之前先确认你的板子系统是 32 位还是 64 位uname -m看一下输出如果是armv7l就用 arm 包如果是aarch64就找 aarch64 的包装反了直接报cannot execute binary file。Hotspot 本身是 Java 默认的虚拟机实现以吞吐量优先在绝大多数后端服务场景下是靠谱的选择。AdoptOpenJDK 还提供过 OpenJ9 版本比如OpenJDK11U-jdk_aarch64_linux_openj9那是 IBM 主导的另一个 JVM启动快、内存占用低但 GC 行为和 Hotspot 差异比较大并不适合所有业务无脑换。所以文件名里有hotspot反而是个定心丸——说明这是最通用的主线版本。1.2 版本号 11.0.8_10 和 tar.gz 格式背后的考量版本号11.0.8_10的含义是Java 11 的第 8 个安全更新版本build 编号是 10。这是 2020 年 7 月发布的版本。放在今天看它已经算“高龄”了但很多遗留系统、老框架、某些国产设备出厂预装环境还在用这个版本。关于格式tar.gz是源码编译产物打的压缩包解压即用不需要安装器不需要 root 权限拷到任意目录配置一下路径就能跑。相比rpm、deb这种封装好的安装包tar.gz 在嵌入式设备上有个天然优势不依赖系统的包管理工具。很多 ARM 板子的出厂系统是精简过的rpm 或者 dpkg 可能都残缺不全这时候 tar.gz 解压大法就是最稳的部署方式。另外在做交叉编译工具链、容器镜像的时候tar.gz 也可以直接拷进镜像当基础层比走安装器流程省事得多。2. 在 ARM Linux 上部署这套 JDK 的实操细节2.1 部署前的环境检查清单拿到这个包以后不要上来就解压先花两分钟确认三件事。第一确认架构。上面说过uname -m看输出。如果显示armv7l说明是 32 位用户空间哪怕 CPU 是 64 位的比如树莓派 4 装了 32 位系统用这个 arm 包没问题。如果显示aarch64那这个包就装不了你得去 Adoptium API 或者镜像站找aarch64_linux的版本。第二确认 glibc 版本。JDK 二进制需要依赖系统的 glibc如果板子的系统太老比如 glibc 2.17 以下新版 JDK 可能直接启动不了报version GLIBC_X not found。看 glibc 版本的命令是ldd --version2.17 是台积电、瑞芯微等常见 BSP 的及格线。这个 11.0.8 的包对 glibc 的要求不算高大部分 ARM Linux 发行版都能跑。第三确认磁盘空间和内存。JDK 11 完整版解压后大约 280MB 左右运行起来 JVM 默认堆大小会按物理内存的 1/4 自动分配。如果你板子只有 1GB 内存建议后面部署服务时显式用-Xmx限定堆大小不然 JVM 可能“乐观”过头把系统内存吃超标导致 OOM Killer 动手。2.2 完整安装步骤与 PATH 配置环境确认没问题之后安装过程其实就三步解压、配置环境变量、验证。如果你是在一台干净的 ARM 设备上操作可以按下面这套来。# 1. 解压到 /usr/local 目录或你习惯的软件安装目录 tar -xzf OpenJDK11U-jdk_arm_linux_hotspot_11.0.8_10.tar.gz sudo mkdir -p /usr/local/java sudo mv jdk-11.0.810 /usr/local/java/jdk-11.0.8 # 2. 配置环境变量 sudo tee /etc/profile.d/jdk.sh EOF export JAVA_HOME/usr/local/java/jdk-11.0.8 export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar EOF # 3. 让环境变量生效并验证 source /etc/profile.d/jdk.sh java -version这里有两处值得说下。CLASSPATH 在 JDK 9 以后其实不是必需品了因为模块化系统在编译和运行时有自己的类加载机制但写上也不影响只是很多老教程的习惯。真正重要的是 Java 11 不再自带 JRE 目录$JAVA_HOME/lib/dt.jar这些文件在 JDK 9 之后也已经被模块化拆散了。换句话说CLASSPATH那行如果你发现路径不存在直接删掉就行不影响任何编译运行。另外验证java -version的时候注意看输出的第二行是OpenJDK 64-Bit Server VM还是OpenJDK Server VM。如果是 64-Bit说明系统是 64 位用户空间你用 arm 包跑出来的反而说明你这包不对——arm 包的 JVM 按 32 位编译的话不会显示 64-Bit。这里别慌重新下载对应 aarch64 包即可。2.3 systemd 服务化部署示例实际项目中装好 JDK 只是第一步更常见的是把这个 JDK 绑定到一个 Spring Boot 或者其他 Java 进程上做成开机自启动服务。这里给一个 systemd 的示例特别是 ARM 设备上跑服务建议用 systemd 而不是直接 nohup 后台跑方便管理崩溃重启和日志。[Unit] DescriptionJava Application Service Afternetwork.target [Service] Userapp EnvironmentJAVA_HOME/usr/local/java/jdk-11.0.8 ExecStart/usr/local/java/jdk-11.0.8/bin/java -Xms256m -Xmx512m -jar /opt/myapp/app.jar Restartalways RestartSec5 [Install] WantedBymulti-user.target注意我用了 Userapp在 ARM 设备上不建议直接用 root 跑业务进程安全性和文件权限管理都是问题。-Xms256m -Xmx512m是做嵌入式/低配服务器时的常用配置——如果板子内存只有 1G512M 的堆上限已经占了一半再多就会引起系统换页GC 频繁性能反而下降。3. 选型解读为什么这个版本值得用以及何时该换掉它3.1 什么时候坚持用旧版 11.0.8Java 11 是 LTS 长期支持版本企业级应用里非常常见。11.0.8 虽然是老版本但有两个场景下它反而是“最优解”。第一个场景是历史系统兼容。不少 ARM 嵌入式产品里跑的是基于 Java 8 或 Java 11 老版本开发的应用这些应用里可能用到了某些内部 API 或者依赖旧版的字节码行为。我实际遇到过用新版 JDK 11.0.2x 跑老应用日志里出现各种IllegalAccessError和反射相关异常换回 11.0.8 就一切正常的情况。如果你没有刚需升级特性旧版只要能满足安全合规要求就别乱动。第二个场景是外设驱动和硬件绑定的需求。ARM 设备往往带着一堆硬件库比如串口通信 jar 包装的 native 库或者工业相机 SDK 的 .so 文件这些 .so 文件可能只针对特定 glibc 版本编译过。升级 JDK 不会影响 .so 本身的调用但 JVM 内部对 native memory 的管理策略在不同版本之间是有差异的导致某些老 SDK 在 JDK 更高版本下启动时直接SIGSEGV。遇到这种玄学问题回退 JDK 小版本是成本最低的排查手段。3.2 什么情况下应该考虑升级或更换发行版反过来如果你是新项目或者说你要部署的是面向公网的 Java 服务我强烈建议至少升级到 11.0.2x 以上的最新 11 版本甚至直接考虑 17 或 21 LTS。原因很直接11.0.8 是 2020 年的版本中间修复了大量安全漏洞包括远程代码执行级别的漏洞。做安全测试时如果你的服务还在 11.0.8 上扫描报告基本是一页红。另外版本选择上现在 AdoptOpenJDK 已经迁移到 Adoptium 项目新版本统一叫 Eclipse Temurin。下载地址变了构建体系也变了但在 tar.gz 部署方式上路数完全一样——解压配 PATH跑起来。如果你对新环境的兼容性没有执念直接上 Temurin 11 LTS 最新版是更稳妥的选择。只有当你明确知道某个老应用依赖 11.0.8 的行为时才继续停留在这个版本上。4. 常见问题排查与避坑方案实录下面这些问题是 ARM Linux 上部署 JDK 时我遇见频率最高、也最容易被忽略的坑按“现象—原因—解决”的格式整理成一个速查表方便你做运维排查时直接对号入座。现象常见原因解决方案java: command not foundPATH 未配置或环境变量未 source检查/etc/profile.d/jdk.sh是否生效执行echo $JAVA_HOME确认cannot execute binary file: Exec format error下载的包和系统架构不匹配uname -m确认是 armv7l 还是 aarch64换对应包Error: Could not create the Java Virtual Machine物理内存不够 未指定-Xmx启动命令显式加-Xmx256m或更小堆限制java.lang.UnsatisfiedLinkError: no xxx in java.library.pathnative .so 库缺失或路径不对export LD_LIBRARY_PATH/opt/native_lib:$LD_LIBRARY_PATH然后重启进程ClassNotFoundException、NoClassDefFoundError类路径不对或 jar 冲突用java -cp显式指定类路径或者检查依赖版本冲突服务运行一段时间后进程被杀系统内存不足触发 OOM Killer降低堆内存上限排查是否有 native 内存泄漏检查dmesg里的 oom 记录java -version显示Error occurred during initialization of VMglibc 版本过旧ldd --version确认 glibc 版本必要时升级系统 BSP4.1 启动报 class file version 错误另外一个值得单拎出来说的是版本不匹配的经典报错你用这个 JDK 11 去跑一个编译目标为 Java 17 的 jar启动时会直接报java.lang.UnsupportedClassVersionError: xxx has been compiled by a more recent version of the Java Runtime如果你手头只有一个 JDK 11但被临时要求跑新编译产物我的建议是先别急着装第二个 JDK。先查一下那个 jar 是不是真的必须跑在 17 上。有些 CI 流程里默认用了新版 JDK 编译明明源码是语法兼容的产物却被标成了高版本 class 文件。这种情况可以用javap -v xxx.class | grep major看版本号major 61 对应 Java 17major 55 对应 Java 11。如果是构建工具打包导致的改一下构建 JDK 版本重新打包比在运行环境里折腾要省事得多。4.2 ARM 上 JVM 性能调优的独家经验最后聊点性能层面的实操。ARM 设备和 x86 服务器不一样CPU 核数少单核频率低内存带宽也有限JVM 默认的参数在 ARM 上往往不是最优解。我实测的经验是在树莓派 44 核 Cortex-A72和 RK3399 上跑同样的 Spring Boot 应用默认 JVM 参数下 GC 停顿时间比 x86 上明显偏长开启-XX:UseContainerSupportJDK 10 默认已开启之外调整为-XX:MaxRAMPercentage75.0这种按比例分配堆的做法比固定-Xmx对内存波动更友好。但请注意如果你用的是 cgroup v1 的老容器运行时环境某些低版本 JDK 的容器感知不准确仍然需要固定-Xmx。另外 ARM 平台通常会有多个不同频率的 CPU 核心比如 big.LITTLE 架构JVM 的线程调度默认没有针对这个做特殊优化。如果你确认服务对延迟敏感可以尝试用taskset把 Java 进程绑定到高性能核心上跑虽然方法比较暴力但在某些设备上实测效果提升明显。至于网上讨论较多的-XX:UseSerialGC和-XX:UseG1GC之争在 ARM 低配设备上我建议堆在 256MB 以下的用 SerialGC 或 ParallelGC堆在 512MB 以上的用 G1。原因很简单——G1 的 Region 管理本身有固定开销在超小堆上反而拖累吞吐真没必要为了“高端技术”牺牲实际性能。4.3 下载源与镜像选择关于下载只说一点。这个包在很多镜像站都有存档但如果你要下载其他版本的 Adoptium/Temurin 包建议直接用 Adoptium 官网的 API 查询或者用国内云厂商的镜像源。文件名要精准匹配arm_linux、aarch64_linux、x64_linux这几个名字没看准就下错浪费的时间往往是小事关键是容易把整个部署节奏打乱。还有一点小提示下载完务必核对一下 SHA256 校验和官方页面和 API 里都有对应哈希。嵌入式环境网络条件大多不太好传一半文件损坏这种破事我碰到过不止一次校验一下真的花不了 10 秒。5. 写在最后的一些实际操作体会这个 11.0.8 的 ARM 版 JDK我在树莓派、瑞芯微 RK3399、飞腾 FT-2000 等好几类设备上都部署过整体感受是tar.gz 格式确实是最不容易踩坑的部署方式只要能确认架构和 glibc 版本解压跑通的成功率接近百分之百。最麻烦的从来不是 JDK 本身而是系统环境不干净导致的各种小意外——缺库、权限、内存不够这都需要一步步排查。根据我个人经验如果你不需要兼容老 SDK我依然建议优先用最新的 OpenJDK 11 LTS 版本或者直接上 17/21。11.0.8 作为特定历史时期的版本在兼容性调试、离线部署、老项目锁定版本这些场景里还有价值但新项目真的没必要从它起步。最后再分享一个实在的小技巧在 ARM 设备上部署完 JDK 后用java -XshowSettings:vm -version快速看一下 JVM 的默认堆设置和运行时参数这样你就能知道当前设备上 JVM“默认乐观到什么程度”再决定要不要显式设置堆大小。这个命令在排查和调优的时候特别好用。本文还有配套的精品资源点击获取
返回列表