Java 16 ARM版JDK安装与交叉编译实战指南
2026/9/3 17:42:33 网站建设 项目流程

简介:这是一份适用于ARM64 Linux平台的JDK十六完整发行包,对应OpenJDK十六点零点一版本,用于在ARM架构设备上运行和开发Java应用程序。资源面向需要在树莓派、飞腾等国产ARM平台部署Java环境的开发者、运维人员,以及搭建我的世界服务器的个人或团队,可解决官方JDK在ARM Linux下获取困难、配置繁琐的问题。压缩包共包含四百五十个文件,总体积约为一百九十三点六六兆字节,内部包含jmod标准模块、so动态链接库、可执行的编译与运行命令工具包以及各类许可证和配置文件,其中既有模块化运行时,也有完整的编译调试工具链,能够满足日常开发、服务端运行与二次封装需求。截至目前已有约一千六百六十五人学习下载,经过较多用户实际使用验证。解压后可直接配置JAVA_HOME与PATH使用,无需自行编译,适合需要快速搭建Java运行环境或部署服务端的场景,是质量可靠的ARM版JDK资源。

1. 为什么大家都在找Java 16的ARM版

最近不少做嵌入式开发、边缘计算,还有国产化部署的朋友都在问同一个问题:Java 16 ARM版到底怎么装?JDK 16的ARM包在哪下载?这个问题看似简单,实际踩坑的人不少。我最早是在一块ARMv8开发板上部署业务系统时开始折腾JDK 16的,当时因为架构没选对,装完直接报“cannot execute binary file”,后面换对了aarch64版本才跑起来。

先说结论:JDK官方从Java 8开始就对ARM架构提供了相对完善的支持,但很多人习惯性地下x86_64的安装包,或者下了ARM 32位的版本,导致在64位ARM平台上一脸懵。Java 16是2021年3月发布的短期版本,虽然已经过了免费公开更新期,但在不少老项目中仍然被锁定,无法轻易升级到更高的JDK版本,这就导致了对Java 16 ARM版的需求至今还在。

这篇文章我打算把ARM架构下JDK 16的下载、安装、配置、踩坑,以及和交叉编译相关的注意事项完整写一遍。内容包括:ARM版JDK的适用场景、aarch64与armhf的区别、如何在Linux ARM设备上正确安装JDK 16、如何在x86主机上为ARM目标平台准备运行环境,以及遇到典型报错时的排查思路。无论你是要在树莓派上跑Java服务,还是要给国产ARM平台部署应用,这篇都能用得上。

需要说明的是,我讲的都是基于公开JDK构建产物和常规Linux ARM环境的实操经验,不涉及任何特殊手段。

2. ARM版JDK的核心区分:aarch64、armhf、armel到底选哪个

ARM架构在JDK的视角里,并不是一个笼统的概念。下载JDK 16 ARM版之前,你首先得搞清楚目标设备的ARM到底是哪一种。这个选错了,后面全是白忙。

2.1 三种常见ARM类型及判断方法

JDK发行版中常见的ARM标识有这三种:

  • aarch64:64位ARM架构,对应ARMv8-A及以上的64位指令集。现在绝大多数服务器级ARM芯片、树莓派4B/5、飞腾、鲲鹏、Apple Silicon的Linux虚拟机,都是aarch64。
  • armhf:32位ARM架构,但要求硬件支持硬浮点(ARMv7及以上)。树莓派2/3的32位系统、大部分老款Android开发板的Linux系统,走的是armhf。
  • armel:32位ARM架构,软浮点,通常跑在非常老的ARMv5/ARMv6设备上。JDK 16官方Linux构建里已经不太提供armel版本,基本可以放弃。

判断你的设备属于哪种,一行命令就够:

uname -m

输出是aarch64,那就是64位ARM;输出是armv7l或armv6l,则是32位ARM,通常配合armhf的包。还有更稳妥的做法,用file命令看系统里随便一个可执行文件,比如file /bin/ls,它会直接告诉你这个系统的ABI类型。

我自己在实际部署中见过最典型的问题,就是把aarch64的设备识别成“ARM”,然后下了32位的包,装完直接段错误。还有一个坑是,某些操作系统发布了多架构的ISO镜像,你安装系统时选了ARM64,但实际内核是32位兼容模式跑的,此时uname -m可能是aarch64但用户态是32位,这种情况比较少见,可以通过getconf LONG_BIT来确认系统用户态的位数。

2.2 JDK 16官方支持的ARM平台

OpenJDK的官方构建,也就是大家通常说的Oracle JDK和OpenJDK上游构建,针对Linux ARM平台提供的是两类:Linux/aarch64和Linux/arm。

  • Oracle JDK 16:官方提供Linux aarch64的tar.gz包,直接下载就能用。32位ARM的官方包在Java 8之后就不再提供了,所以JDK 16没有官方armhf版本。
  • Adoptium(也就是Eclipse Adoptium项目,前身是AdoptOpenJDK):提供Linux/aarch64的JDK 16构建,同时也有Linux/arm 32位版本(基于armhf),但更新节奏和官方相比略有滞后,不过对大多数场景完全够用。
  • 中兴、阿里、华为等国内厂商也各自维护着基于OpenJDK的ARM版本,像BiSheng JDK、Dragonwell等,但通常更偏向更高版本的JDK,JDK 16这个版本点位的维护相对少。

所以结论是:如果你的ARM设备是64位的,JDK 16有非常干净的官方构建可选;如果是32位ARM设备,你需要去找Adoptium的arm版本,或者考虑用BellSoft Liberica JDK提供的armhf构建。

2.3 为什么JDK 16 ARM版值得专门写一篇

Java每个版本都会有对应的ARM版本,但JDK 16比较特殊。一方面它处在Java 11和Java 17两个LTS版本之间,属于一个“政策敏感期”的版本——很多公司内部框架当时是基于Java 16开发并上线的,后期由于合规要求不能直接跳到Java 17,这就造成了JDK 16 ARM版的存量需求。另一方面,JDK 16引入了很多对后续版本有深远影响的特性,比如Records、Pattern Matching for instanceof、Sealed Classes(预览)等,不少老项目虽然跑在Java 11上,但会先用JDK 16做兼容性验证。

如果你手头正好有这类项目,并且需要部署到ARM平台,那我建议你直接跳过源码编译路线,优先使用官方或Adoptium的二进制构建。从源码编译OpenJDK 16到ARM平台是一件非常耗时的事,依赖一堆工具链和库,在没有充分理由的情况下,不推荐自己折腾。

3. JDK 16 ARM版的下载来源与选择策略

3.1 主流下载渠道一览

我整理了目前能稳定获取JDK 16 ARM版的几个渠道,按推荐程度排序:

渠道架构支持说明
Oracle JDK 16存档aarch64需Oracle账号,存档页可下载,适合生产环境合规需求
Adoptium APIaarch64、arm无需登录,直接API下载,社区活跃
BellSoft Liberica JDKaarch64、armhf提供多种打包格式,含JDK 16的arm版本
阿里龙井Dragonwellaarch64长期维护,但JDK 16版本只保留了一段时间
源码编译全架构OpenJDK 16源码在官方仓库,可自行交叉编译

如果你是个人开发者,我建议直接走Adoptium的API下载,完全免费,无需注册。比如要下载JDK 16 Linux aarch64的tar.gz,可以用这个请求模板:

curl -L "https://api.adoptium.net/v3/binary/latest/16/ga/linux/aarch64/jdk/hotspot/normal/eclipse"

这条命令会直接给你一个tar.gz的二进制包。如果你想先看有哪些版本可选,可以访问:

curl -s "https://api.adoptium.net/v3/assets/latest/16/ga/linux/aarch64/jdk/hotspot?project=jdk"

返回的JSON里会有完整的下载链接、SHA256校验值、文件大小等信息。注意,Adoptium的API支持/latest/16/这种写法,但如果你需要精确到某个构建版本,可以把latest换成具体的版本号,比如16.0.2+7

3.2 下载后必须做的校验

很多人下载完JDK就直接解压了,我建议你至少花10秒钟做一下文件校验。不管是Oracle还是Adoptium提供的包,都会带上SHA256值。Linux下校验方式如下:

sha256sum jdk-16.0.2_linux-aarch64_bin.tar.gz

把这个结果和下载页面标注的SHA256对比一下,如果一致再继续。不要小看这一步,我曾经在某个第三方镜像站下载过被篡改过的JDK包,解压后明显有异常进程行为,从那以后我所有下载的JDK都会做校验。至少在使用非官方渠道下载时,这一步是必须的。

3.3 Oracle JDK与OpenJDK构建的选择

关于选Oracle JDK还是OpenJDK构建,很多人的直觉是“Oracle的更稳定”,但放到JDK 16这个版本上,区别没有想象中那么大。Oracle JDK 16和OpenJDK 16的主要区别在于:Oracle JDK附带了一些商业特性(比如Java Flight Recorder在JDK 11时还是商业版,到JDK 16基本都开源了)以及Oracle的商标和证书声明。如果你只是跑应用,Adoptium的OpenJDK构建完全够用。

生产环境我个人的标准是:如果公司有合规要求,就下Oracle存档版;如果是在多云或容器环境里跑,Adoptium的镜像更顺手。容器方面,如果你用的是Docker,可以直接参考eclipse-temurin:16-jdk镜像,它提供了arm64架构的镜像,拉取时会自动匹配宿主机架构,省去手动下载的麻烦。

4. ARM Linux主机上安装JDK 16的全流程

4.1 环境确认与依赖检查

在安装之前,先把环境摸清楚。我一般会跑这么一组命令:

cat /etc/os-release uname -m ldd --version | head -1 free -h df -h /opt

第一条看操作系统发行版和版本,第二条看架构,第三条看glibc版本,第四条看内存,第五条看磁盘空间。JDK 16本身对内存要求并不高,512MB内存的小设备也能跑,但编译类应用或者跑JVM的G1垃圾回收器时,内存太小会影响性能,小于256MB的设备我会建议用Serial GC。

glibc版本是个容易被忽视的坑。官方JDK 16的aarch64构建依赖glibc 2.17以上,如果你用的是比较老的嵌入式发行版,比如CentOS 7系列的ARM版本,glibc版本可能不够,这时装完JDK会直接报version GLIBC_2.18 not found之类的链接错误。如果你遇到这种情况,优先考虑升级系统基础库,或者在项目层面换用JRE精简镜像,再不行就只能自己找低glibc依赖的构建。

4.2 安装步骤详细说明

这里我以Adoptium的JDK 16 aarch64包为例,展示完整安装过程。

先在任意目录下载:

wget "https://api.adoptium.net/v3/binary/latest/16/ga/linux/aarch64/jdk/hotspot/normal/eclipse"

注意,下载下来的文件名可能是eclipse,完全没有扩展名,需要手动重命名一下:

mv eclipse jdk-16-arm64.tar.gz

然后创建目标目录并解压:

sudo mkdir -p /usr/local/java sudo tar -xzf jdk-16-arm64.tar.gz -C /usr/local/java

解压后目录名一般是jdk-16.0.2+7这样的格式,做一层软链方便后续版本切换:

sudo ln -s /usr/local/java/jdk-16.0.2+7 /usr/local/java/jdk-16

配置环境变量。我是建议单独新建一个/etc/profile.d/jdk16.sh来管理,而不是直接改/etc/profile,这样后续换版本时不需要翻大文件。

sudo cat > /etc/profile.d/jdk16.sh << 'EOF' export JAVA_HOME=/usr/local/java/jdk-16 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib EOF

然后让配置生效:

source /etc/profile.d/jdk16.sh

验证是否成功:

java -version javac -version

如果输出类似这样,说明安装成功:

openjdk version "16.0.2" 2021-07-20 OpenJDK Runtime Environment (build 16.0.2+7-7) OpenJDK 64-Bit Server VM (build 16.0.2+7-7, mixed mode, sharing)

4.3 静态安装与容器化安装的取舍

ARM设备上安装JDK,除了常规的tar.gz解压,还有两种常见方式:用包管理器安装,或者直接用容器镜像。

部分ARM Linux发行版自带OpenJDK 16的包,比如Ubuntu 21.04的aarch64仓库里就有openjdk-16-jdk,可以直接用apt install openjdk-16-jdk装好。但注意,发行版仓库里的JDK版本更新节奏比较慢,且不一定是最新安全更新,适合学习环境,生产环境我还是推荐手动解压官方包,至少你能精确掌控补丁版本。

容器化是我个人比较推荐的方式,尤其是在云边协同场景中。参考一下这个Dockerfile:

FROM eclipse-temurin:16-jdk WORKDIR /app COPY target/myapp.jar . CMD ["java", "-jar", "myapp.jar"]

在ARM64服务器上直接构建,Docker会自动拉取arm64/v8架构的镜像,不需要手动关心架构问题。这种方式在构建一次、多平台复用方面有明显优势。如果你需要同时支持x86和ARM,可以了解下docker buildx,它支持多平台镜像构建,一步到位。

4.4 多版本JDK共存管理

ARM设备上同一个环境装多个JDK是很常见的事。我自己的习惯是用update-alternatives来管理。Debian/Ubuntu系自带这个工具,装完多个JDK后,执行:

sudo update-alternatives --config java

它会列出所有已经注册的java可执行文件,你选择默认要用的那个,系统会自动切换。还有更优雅的方案是用SDKMAN,它可以管理多个JDK版本,支持ARM架构,安装切换都非常方便。

5. 交叉编译场景下JDK 16 ARM版的正确打开方式

5.1 什么是交叉编译,JDK跟它是什么关系

“arm交叉编译”这个热词经常和JDK出现在一起,但很多人混淆了两个概念。

  • 第一种场景:你在x86开发机上写Java代码,目标运行平台是ARM设备。Java的跨平台特性使这个场景根本不需要交叉编译器——你只需要在x86上编译出.class字节码文件,然后把JRE(JDK的运行时部分)放到ARM设备上,Copy整个应用过去就能跑。字节码是平台无关的,不用交叉编译。
  • 第二种场景:你需要在x86开发机上构建出能在ARM上运行的OpenJDK本身。这才是真正的交叉编译,需要配置完整的交叉编译工具链,过程复杂得多。

大多数普通开发者的需求是第一种,完全不需要交叉编译。JDK 16 ARM版在这个场景中的角色是“运行时”,而不是“编译工具链”。所以如果你只是写业务代码,就不要被“arm交叉编译”这个搜索词带偏了。

5.2 JDK 16在x86主机上为ARM目标构建应用

你的开发机是x86_64,目标设备是ARM64,构建流程应该是这样:

  1. 开发机安装任意平台的JDK 16,x86_64就行。
  2. 用Maven或Gradle正常构建,产出可执行的JAR包。
  3. 把JAR包和对应ARM架构的JRE一起打包,分发到目标设备。
  4. 目标设备解压后直接java -jar运行。

这里要提醒的是,如果你用到了JNI(Java Native Interface),也就是调用了本地代码(.so文件),那就必须为ARM架构单独编译那些本地库,JAR包里的Java部分不做交叉编译,但.so需要。这属于另一个层面的交叉编译,不在JDK本身的范畴。

我自己有一个小工具脚本,专门用来给ARM设备打包运行环境。核心逻辑就是先解压ARM版JDK,然后把项目JAR和依赖包放进去,再写一个启动脚本:

#!/bin/bash # build-arm-runtime.sh # 将当前项目打成适用ARM64的完整运行包 set -e PROJECT_HOME=$(pwd) OUTPUT_DIR=dist-arm64 JDK_TGZ=jdk-16-arm64.tar.gz APP_JAR=myapp.jar mkdir -p $OUTPUT_DIR tar -xzf $JDK_TGZ -C $OUTPUT_DIR cp $APP_JAR $OUTPUT_DIR/ cat > $OUTPUT_DIR/start.sh << EOF #!/bin/bash DIR=\$(cd \$(dirname \$0) && pwd) \$DIR/bin/java -jar \$DIR/myapp.jar EOF chmod +x $OUTPUT_DIR/start.sh

这个脚本在生产环境中救过我很多次,每次部署新版本只要重新跑一遍就行。

5.3 真正需要在ARM上编译JDK的场景

如果你确实需要从源码编译OpenJDK 16到ARM平台,通常是因为:

  • 找不到目标架构的现成二进制构建。
  • 需要定制JVM特性,比如修改GC参数或增加特定补丁。
  • 目标系统太老,没有适合的glibc,需要静态链接。

这种情况下,你需要准备一个交叉编译工具链。在x86_64的Ubuntu上交叉编译aarch64的OpenJDK,典型的依赖安装命令是:

sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

然后configure时要指定交叉编译参数:

bash configure \ --openjdk-target=aarch64-linux-gnu \ --with-jvm-variants=server \ --with-boot-jdk=/usr/lib/jvm/java-16-openjdk-amd64 \ --with-toolchain-path=/usr/aarch64-linux-gnu

这里的关键细节是--with-boot-jdk必须指向一个能运行的JDK,且版本要小于等于目标版本。因为OpenJDK编译有个“自举”过程:先用已有的JDK编译出新JDK的一部分,再用新JDK去编译剩下的部分。如果boot JDK版本太新,编译过程会报错。

我自己交叉编译过一次OpenJDK 16到aarch64,整个过程大概花了40分钟,主要时间都耗在配置依赖和等待编译上。我的建议是,除非你有非常特殊的需求,否则直接用官方预编译的ARM版JDK 16,省下来的时间用来写业务代码更有价值。

6. 部署JDK 16 ARM版后的典型问题与排查

6.1 运行环境类问题

  • 问题1:cannot execute binary file: Exec format error

    这个报错最常见的原因就是架构不匹配,比如在aarch64系统上执行了x86_64的JDK二进制。解决方式:运行uname -m确认系统架构,然后重新下载对应架构的JDK。

  • 问题2:libjli.so: cannot open shared object file

    这类问题一般是因为JDK解压后目录结构被改动过,或者环境变量指向了错误的路径。检查一下JAVA_HOME是否指向了JDK的完整根目录,不要指向bin目录。

  • 问题3:Error: Could not create the Java Virtual Machine

    优先排查内存配置。ARM小设备上默认的堆内存策略可能不合适,可以手动加上-Xmx256m这样的参数限制堆大小。我曾在128MB内存的设备上遇到过类似问题,把堆大小限制到64MB后就能跑起来了。

6.2 JDK版本和系统库交互问题

  • 问题4:Error: dl failure on line 893

    这个错误在嵌入式Linux上遇到得比较多,跟JDK加载本地库时依赖的libz等系统库有关。排查步骤:

    ldd $JAVA_HOME/bin/java

    看输出里有没有not found的行。如果有,就是系统缺少对应的共享库,比如libz.so.1,用apt install zlib1gyum install zlib装一下即可。

  • 问题5:证书异常

    JDK 16 ARM版自带cacerts证书库,但如果你在ARM设备上访问HTTPS接口时报证书错误,先确认是不是系统时间不对。ARM设备很多都不带RTC电池,重启后时间是1970年,证书直接判过期,这个问题我遇到过好几次。

6.3 常见问题速查表

症状主要原因处理方式
Exec format error架构不匹配uname -m确认后重下包
No such file or directory(文件存在时)缺少动态链接库或解释器ldd检查动态库
Java VM无法启动内存过小或配置错误增加-Xmx限堆,或用Serial GC
证书错误系统时间不对或cacerts损坏校时或重新导入证书
高CPU占用容器或小设备上GC频繁根据场景选择合适的GC策略
启动慢熵源不足安装haveged服务

6.4 我实际踩过的一个典型坑

之前我在一块基于RK3399的开发板上部署Java 16服务,启动时一直报Unable to obtain from local environment。后来排查发现,系统缺乏足够的随机数熵源,JVM启动时需要读取/dev/random,而嵌入式设备上/dev/random的熵池很快就耗尽了,导致启动卡死或异常。

解决方法是安装haveged服务,让系统可以从硬件噪声中持续生成随机数:

sudo apt install haveged sudo systemctl enable haveged sudo systemctl start haveged

换了之后JVM启动速度快了非常多。这个问题在云上很少遇到,但在ARM嵌入设备上很常见,如果你在部署时遇到莫名其妙的卡顿、超时,可以考虑是不是熵源不足。

7. ARM版JDK 16与后续版本的实际选择建议

7.1 JDK 16还是JDK 17

很多人在来找JDK 16 ARM版的时候,其实并不一定真的只能用JDK 16。JDK 17是2021年9月发布的LTS版本,免费公开更新会更持久。如果你的项目没有绑定Java 16特有的API,我非常建议直接上JDK 17的ARM版。JDK 16到JDK 17的迁移成本很低,主要就是一些废弃API的移除和模块化调整。我在多个项目里做过平滑迁移,绝大部分代码连改都不用改。

如果你的项目因为历史原因必须锁定JDK 16,那也要注意,JDK 16在2021年12月之后就不再提供免费公开更新,使用上需要评估安全和稳定性风险。建议至少跟进Adoptium社区的安全公告,如果有高危漏洞,及时在代码层面做规避。

7.2 32位ARM设备上的JDK选择困境

如果你的目标设备是32位ARM,比如树莓派2/3的32位系统,JDK 16的可选方案就明显变少了。我个人的建议是:

  • 能用64位系统就尽量用64位系统。树莓派3及以上的设备都可以直接装64位系统,这也是成本最低的解决方案。
  • 如果确实只能用32位系统,JDK 11的最后一个ARM 32位版本是比较稳定的选择,JDK 16的32位构建可用但选择少。
  • 关注Azul Zulu Prime或BellSoft Liberica的ARM 32位构建,这两家在维护旧版JDK方面比较积极。

7.3 一套针对ARM JDK的验证清单

在ARM设备上装好JDK 16之后,建议跑一遍下面这些检查,确认环境没问题再开始部署业务:

java -version javac -version echo 'public class Test { public static void main(String[] a) { System.out.println("ARM JDK OK"); } }' > Test.java javac Test.java java Test

跑通Test.java说明基本的编译和运行链路是通的。更深入的验证可以这样:

jcmd 0 JFR.start jcmd 0 JFR.dump filename=/tmp/test.jfr

JDK 16里JFR(Java Flight Recorder)已经面向生产可用,能在ARM设备上跑起来,说明JVM的核心功能都正常。如果只需要极简运行环境,还可以使用Java 16自带的jlink工具来裁剪运行时,生成一个只包含所需模块的精简JRE,这个在ARM嵌入式场景中特别有用。

下面是jlink的一个实践示例:

$JAVA_HOME/bin/jlink \ --module-path $JAVA_HOME/jmods \ --add-modules java.base,java.sql,java.naming \ --output /opt/jre-16-min

比如一个典型的Spring Boot应用,裁剪后能从原来的300MB JDK缩减到80MB左右,对存储受限的ARM设备非常友好。裁剪完的运行时用/opt/jre-16-min/bin/java -jar app.jar启动即可。

7.4 我个人的实践体会

从我手上跑过的ARM设备来看,JDK在ARM上的稳定性其实已经相当成熟了。Spring Boot、Netty、Kafka这些常用Java中间件在ARM上跑得很稳。较早的时候ARM上跑JVM的坑主要在:GC在低内存设备上的表现、JIT编译器的优化能力、以及部分依赖本地代码的库没有ARM版本。但发展到JDK 16这个阶段,绝大部分问题都已经解决掉了。

如果你是要部署到树莓派这类个人项目设备上,JDK 16 ARM版配合jlink裁剪,完全够用;如果是企业级生产环境,我会认真评估是否需要LTS版本,并在部署前做好压测和性能基线记录。最后再分享一个小技巧:ARM设备上的JVM,如果内存紧张,优先尝试-XX:+UseSerialGC,它在单核低内存设备上的表现经常会让你意外。

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

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

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

立即咨询