ARM 服务器上安装配置 Java 8:jdk-8u391-linux-aarch64 全指南 简介面向基于ARM架构的Linuxaarch64服务器、嵌入式设备与边缘计算终端这份jdk-8u391-linux-aarch64.tar是Java 8更新391版的完整安装压缩包可帮助开发者在ARM平台上快速搭建Java编译、运行与调试环境解决常规x86 JDK无法在ARM设备上安装的兼容性问题。包内共403个文件压缩后大小约71.23MB主要由java、javac、keytool等可执行工具so动态链接库jar核心库properties配置ttf字体以及HTML、man手册等构成覆盖JDK运行所需的命令行工具、类库、界面资源与认证材料目录结构清晰便于按模块定位。其中可执行工具集成了编译器、调试器、打包与证书管理等功能动态库支撑JVM底层运行配置与字体文件保障图形界面和网络服务正常工作。截至目前已有1486人学习下载。解压后配合JAVA_HOME、PATH等环境变量配置即可投入使用适合需要离线安装、批量部署或后期按模块裁剪的开发者使用同时内置的man手册与HTML文档页可为二次开发和排错提供参考。1. ARM 服务器上的 Java 8兜兜转转还得用它这几年 ARM 架构的机器在云上和边缘侧越来越多跑 Linux aarch64 环境早就不是稀罕事。但真正把 Java 应用往 ARM 上迁移时很多人会卡在同一个问题手上那套跑得好好的 JDK 8 在 x86_64 上能用换个架构就抓瞎。这个 jdk-8u391-linux-aarch64.tar 就是解决这个场景的它是一份带标准目录结构的 Java 8 开发工具包二进制包专门给 aarch64 Linux 环境用。简单说你需要在一个不支持 x86 二进制的 ARM Linux 上把 Java 8 跑起来这个包就是最直接的那条路。适合谁在 ARM 云主机、开发板、国产化服务器上维护老项目的工程师或者正准备把 Spring Boot 等老版本应用迁移到 ARM 环境的人。它不解决业务问题但解决第一道门槛让 java 命令先能跑起来。2. 认识 jdk-8u391-linux-aarch64.tar包内结构与选型逻辑2.1 从 x86_64 切到 aarch64JDK 分发版差在哪先明确一点JDK 是跟 CPU 指令集强绑定的东西。x86_64 的 JDK 是 64 位的 x86 指令拿到 aarch64 机器上完全没法执行反过来也一样。很多人第一反应是“都是 Linux 的应该通用”结果解压后一执行直接报 “Exec format error” 或者 “cannot execute binary file”这就是架构不匹配。aarch64 是 ARM 64 位指令集常见叫法还有 ARM64。这个 jdk-8u391-linux-aarch64.tar 面就非常明确Linux 平台、aarch64 架构、Java 8 的 u391 版本。u391 是 Java 8 一个重要的更新版本修了不少安全漏洞也是目前很多维护项目会选的一个基准版本。选型的时候很多人会纠结到底用 OpenJDK 还是商业发行版。实际上这个 tar 的结构跟标准 JDK 完全一致包含 bin、lib、include、jre 等目录不存在“特殊版本”的额外配置。我一般优先用这种官方目录结构的二进制包原因很直接无论你后面配 Maven、Gradle 还是打 Docker 镜像它跟 x86_64 上的 JDK 路径结构完全对齐迁移成本低到可以忽略。2.2 包内目录结构和关键文件拿到这个 tar 文件后先别急着解压看一眼包结构心里更有底。我们可以用 tar 命令列一下内容tar -tzf jdk-8u391-linux-aarch64.tar | head -20输出是包内文件清单的头 20 条。命令的-t表示列出内容而不是解压-z表示通过 gzip 解压读取-f指定文件名。看到类似jdk1.8.0_391/bin/java、jdk1.8.0_391/lib/rt.jar这种结构就确认是标准 JDK 目录包。目录里最关键的内容是这几个路径作用bin/javaJava 运行时入口执行java -version的就是它bin/javacJava 编译器用于把.java编译成.classlib/rt.jarJava 运行时核心类库lib/tools.jar编译器和一些开发工具依赖的类库jre/独立运行时目录有的程序会单独指定 JRE 路径include/JNI 开发时需要的 C/C 头文件这套结构跟 x86_64 版 JDK 几乎一致所以你不需要学一套新的部署方式。对于纯跑应用的人来说bin 和 lib 就够用了如果你还要做 JNI 或者写 Java agentinclude 目录里的头文件同样不能少。另外每个文件本身是否完整值得单独验证一下这放在下一章一起说。3. 安装与配置从 tar 包到可用的 java 命令3.1 校验、解压、目录归属拿到 tar 包后第一步永远是校验完整性。常见做法是先核对 SHA256 校验值确保传输过程没有损坏。我习惯先跑一下sha256sum jdk-8u391-linux-aarch64.tar把这行命令输出的 64 位十六进制字符串跟下载时发布的校验值逐位对比。这一步是玄学重灾区很多人图省事跳过结果解压后 java 二进制损坏报了各种莫名其妙的java.lang.UnsatisfiedLinkError最后才想起来校验文件。校验通过后解压到 /usr/local 或者自己的用户目录。以 /usr/local 为例sudo tar -xzf jdk-8u391-linux-aarch64.tar -C /usr/local-x是解压-z走 gzip 解压-C指定解压目标目录。解压完成后目录会出现在/usr/local/jdk1.8.0_391。这个路径是不是固定取决于 tar 包内部的顶层目录名。用tar -tzf列过之后就能确认我这里是这个版本常见的叫法。建议把目录归属改成专用用户避免后面应用以 root 跑。常见做法是创建一个java用户或者直接用已有业务用户sudo chown -R java:java /usr/local/jdk1.8.0_391如果只是单机本地开发这一步可以省但服务器上还是建议做毕竟后面 JAVA_HOME 环境变量可能被多个服务引用权限太开放容易出问题。3.2 设置 JAVA_HOME 与 PATH让系统认账解压完成不代表 java 命令能用因为 shell 默认去/usr/bin找可执行文件。最常见的做法是把 JDK 的 bin 目录追加到 PATH 前面。以 bash 为例编辑用户级配置文件vim ~/.bashrc在文件末尾加以下几行export JAVA_HOME/usr/local/jdk1.8.0_391 export PATH$JAVA_HOME/bin:$PATH export CLASSPATH.:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar第一行定义 JAVA_HOME 为 JDK 根目录很多框架和工具比如 Tomcat、Maven、Gradle都会读取这个变量来确定用哪套 JDK。第二行把 JDK 的 bin 目录插到 PATH 最前面这样执行 java 时优先找到我们指定的版本。第三行设置 CLASSPATH把当前目录和核心类库加进去老项目里很常见。修改完后让配置生效source ~/.bashrc注意source只在当前 shell 生效如果开了新终端那新终端自己会读 .bashrc。服务器上如果跑的是 systemd 服务不能只靠 .bashrc需要把 JAVA_HOME 写进服务的 Environment 配置或者写到 /etc/environment。这个细节后面避坑章还会说。3.3 验证安装java -version 的输出该长什么样配置完先别急着跑项目用最简单的命令确认环境真的对了java -version正确的输出应该是类似这样的java version 1.8.0_391 Java(TM) SE Runtime Environment (build 1.8.0_391-b13) Java HotSpot(TM) 64-Bit Server VM (build 25.391-b13, mixed mode)看到64-Bit Server VM就说明架构对了因为这是 aarch64 的 64 位虚拟机。如果你的输出里写着什么java version 1.7.0或者OpenJDK Runtime Environment但版本数字不对多半是 PATH 里还有别的 JDK 占着优先级。同时确认编译器也正常javac -version输出javac 1.8.0_391即可。把这两条命令组合起来用which java看一下实际路径which java如果输出是/usr/local/jdk1.8.0_391/bin/java说明 PATH 生效。如果输出是/usr/bin/java那就是系统里装过别的 JDK你在第 4 章会看到怎么处理。4. 避坑ARM 下 Java 8 的常见翻车现场4.1 现象明明解压了java -version 报错 “cannot execute binary file”第一次在 aarch64 机器上跑java -version直接报错cannot execute binary file: Exec format error。原因文件架构和系统架构不一致。最常见的情况是系统装的是 aarch64 内核但你下载的 tar 是 x86_64 版或者是 32 位 ARM 系统但 JDK 是 aarch64 的。用uname -m看一下系统架构若输出aarch64就必须用 aarch64 的 JDK 包。解决确认当前系统的架构再重新拿对的 tar 包。这个包的文件名里已经写了 linux-aarch64只要确认下载的不是 x86 版就行。4.2 现象No such file or directory但文件明明存在执行/usr/local/jdk1.8.0_391/bin/java时提示 No such file or directory但 ls 一下文件明明在。原因这个提示不是指 java 文件不存在而是指 java 这个可执行文件依赖的动态链接库找不到。在 aarch64 Linux 上常见原因是系统缺少 64 位 ARM 版的 glibc或者系统是精简版内核连基本的ld-linux-aarch64.so.1都没有。解决检查系统是否安装了对应架构的 glibcldd /usr/local/jdk1.8.0_391/bin/java如果输出里一堆not found就说明动态库缺失。在基于 Debian 的系统上补装基础运行库sudo apt-get install libc6:arm64然后重跑 ldd 确认没有 pending 的依赖项。4.3 现象版本对不上程序却用到了系统自带的 OpenJDK配好 JAVA_HOME 后echo $JAVA_HOME显示正确但跑应用时日志里 Java 版本还是旧的。原因应用是通过 systemd 启动的早读过了/etc/init.d脚本或 service 文件里的环境变量而source ~/.bashrc并没有改掉服务级的环境。解决在 systemd service 文件里显式写环境变量EnvironmentJAVA_HOME/usr/local/jdk1.8.0_391 EnvironmentPATH/usr/local/jdk1.8.0_391/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin改完执行systemctl daemon-reload再重启服务。如果项目用脚本启动把 export 写在脚本开头别依赖用户 profile。4.4 现象JAVA_HOME 配了脚本还是找不到 javalocalhost 上 java 能跑但一执行项目里的 start.sh 就提示java: command not found。原因脚本可能是通过/bin/sh执行的而 /bin/sh 在某些系统上并不加载用户的 .bashrc甚至也不加载 .profile。解决在脚本开头显式指定环境#!/bin/bash export JAVA_HOME/usr/local/jdk1.8.0_391 export PATH$JAVA_HOME/bin:$PATH把这一行作为启动脚本的第一部分保证无论谁调用都能拿到路径。还有个更稳的办法直接在脚本里调 JDK 的全路径/usr/local/jdk1.8.0_391/bin/java不依赖 PATH。4.5 现象GC 和内存参数在 ARM 上突然不生效在 x86 环境上调好的 JVM 参数比如-Xms2048m -Xmx2048m -XX:UseG1GC迁到 ARM 上以后有时日志里 GC 策略不对劲或者启动直接失败。原因不同架构上 JVM 的默认 GC 有差异JDK 8 在 aarch64 上的默认 GC 是分代并行 GC 或 G1取决于具体版本和内存大小。G1 参数在 ARM 上表现正常但如果崩溃了先排查是不是 C1/C2 编译器阈值参数写太激进。解决先用java -XX:PrintFlagsFinal -version | grep GC看默认 GC再按需显式声明。我一般会在启动脚本里把 GC 和堆参数写成显式组合不靠默认值。ARM 上比较稳的配置是java -Xms1024m -Xmx1024m -XX:UseG1GC -XX:MaxGCPauseMillis200如果 JVM 直接无法启动先去掉所有 GC 参数只留堆大小逐层加回来定位是哪一个是雷。5. 让 JDK 8 在一个项目里真正跑起来验证脚本与进阶配置5.1 快速自检脚本把 JVM 参数和架构一次性打出来环境配好后我习惯性写一个自检脚本放在项目根目录下每次部署后执行一遍直接确认当前跑的是哪套 JDK、架构、堆参数。#!/bin/bash echo Architecture Check uname -m echo Java Version java -version 21 echo JAVA_HOME echo $JAVA_HOME echo Effective JDK Path which java echo Default GC java -XX:PrintFlagsFinal -version 21 | grep -i UseG1GC\|UseParallelGC\|UseSerialGC\|UseConcMarkSweepGC这段脚本做了四件事第一块输出系统架构确认是不是 aarch64第二块打印 Java 版本第三块输出 JAVA_HOME第四块打印哪个 GC 标志被启用。输出里如果出现bool UseG1GC true说明 G1 是当前生效的而其他同类的变量为 false。这套脚本的价值在部署新环境时一目了然不用再挨个手敲命令。5.2 配合 Maven/Gradle 把编译目标钉在 Java 8跑老项目时光有 JDK 还不行构建工具经常会悄悄用更高版本的 JDK 来编译结果编译出来的 class 文件版本不对运行时抛UnsupportedClassVersionError。Maven 项目里我一般这样约束编译目标和源码版本。在 pom.xml 的 properties 区域显式声明maven.compiler.source1.8/maven.compiler.source maven.compiler.target1.8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding这三个配置分别指定源码兼容 Java 8 语法、编译目标字节码版本为 1.8、编码格式为 UTF-8。如果你用的编译器插件是默认版本加这两个属性就够如果还不行就显式指定编译插件版本并配置release8。对于 Gradle 项目在 build.gradle 里写java { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }这段的作用同样是锁定编译级别。这一步容易被忽略但实际踩过的人都知道代码在 x86 上编译没问题换到 ARM 上构建后跑不起来十有八九是构建工具用了宿主机默认 JDK根本没读你配的 JAVA_HOME。5.3 把关键启动参数固定成一个 start.sh 模板项目里我习惯放一份 start.sh不依赖系统环境直接把 JDK 路径和参数写死省得到每个新环境都要重新猜路径。一个适用于 JDK 8 在 aarch64 Linux 上的启动模板是这样#!/bin/bash export JAVA_HOME/usr/local/jdk1.8.0_391 export PATH$JAVA_HOME/bin:$PATH APP_HOME/opt/myapp MAIN_CLASScom.example.MyApp $JAVA_HOME/bin/java \ -Xms1024m \ -Xmx1024m \ -XX:UseG1GC \ -XX:MaxMetaspaceSize256m \ -Dfile.encodingUTF-8 \ -Djava.library.path$APP_HOME/lib \ -cp $APP_HOME/lib/*:$APP_HOME/classes \ $MAIN_CLASS-Xms 和 -Xmx 是堆内存初始值和最大值建议保持一致防止动态扩容导致性能抖动-XX:UseG1GC 显式选择 G1-Dfile.encoding 是为了让老代码里依赖默认字符集的逻辑不乱踩坑-Djava.library.path 是 JNI 或 OpenCV 一类 native 库的定位路径。-cp把项目依赖 jar 包和编译后的 class 目录全接上。这套写法不管 shell 是从哪种模式调用都能保证 java 命令来自我们自己指定的 JDK。5.4 收尾真正的验证是部署一次完整应用说到最后我还是留一个习惯哪怕 java -version 已经完美了我仍然会强迫自己走一遍真实应用的启动链路而不是停在“命令能跑”就收工。因为 ARM 上 JDK 8 的坑往往不在 java 本身而在 shared library、字符集、时区数据这些周边环境里。从那以后每次新部署我都强制跑一次完整应用启动然后用jcmd打一次线程快照确认 JVM 真的活起来再让测试进来。希望帮到你这套 JDK 8 在 aarch64 上跑老项目的套路试过就知道比想象中省事得多。本文还有配套的精品资源点击获取