☰
JDK16 ARM版安装实战:AArch64与armhf架构选择及排坑指南
2026/10/10 18:22:48 网站建设 项目流程

简介:面向 ARM64 架构 Linux 平台的 JDK16 开发套件,对应官方 OpenJDK16U-jdk_aarch64_linux_hotspot_16.0.1_9.tar.gz,适配树莓派、鲲鹏等 aarch64 服务器,满足在 ARM 环境运行 Java 16 程序或搭建我的世界 1.17.1 服务端的开发与部署需求。整个 gz 压缩包共 450 个文件,大小约 193.66MB,核心组件包括 71 个 jmod 模块描述、70 个 license 许可、37 个 so 本地动态库,以及 java、javac、jlink、jpackage、jshell 等常用命令,辅以 h 头文件、properties 配置、security/policy 安全策略和 31 个 md 文档,目录结构标准完整。已有 1665 人浏览学习此资源。下载后配置 JAVA_HOME 与 PATH 即可直接使用,免去自行编译 OpenJDK 的流程,适合在低功耗 ARM 设备上快速部署 Java 服务或 MC 服务端;同时利用 jlink 可以裁剪出更小的自定义运行时,为后续打包发布提供便利。 2021年3月发布的Java 16,在Java版本序列里算是个有点“过渡”的位置:它既不像Java 11那样被当成长期支持版本,也不像后面的Java 17那样一出来就直接面向生产环境。但它在ARM平台上的意义,反而比很多人想象中重要。那段时间我恰好在一台飞腾处理器的工控机上部署Java服务,随手从网上找了一个JDK包,结果不是Exec format error就是glibc版本不匹配,折腾到半夜才弄明白问题不是出在JDK本身,而是ARM平台的架构区分远比我想的细。这篇博文就围绕JDK 16 ARM版,把下载、安装、验证和排坑的全过程都梳理一遍,适合嵌入式开发者、ARM服务器运维人员,以及想在树莓派或国产ARM设备上跑Java服务的朋友参考。

1. 为什么ARM机器上的JDK不能随便下载

1.1 ARM和x86_64,从底层就是两种生物

先别急着下载,下载之前需要先弄清楚目标机器的CPU指令集。ARM和x86_64的差异,可以拿柴油发动机和汽油发动机来类比:虽然都是驱动汽车,但内部工作原理完全不同,油加错了车就趴窝。JDK也是这样,官网上下载的linux-x64后缀安装包,生成的是x86_64指令,你把它放到ARM处理器上执行,系统根本不认识这些指令,直接抛出cannot execute binary file。

很多第一次在ARM设备上部署Java的人都会栽在这个地方。因为Java号称“一次编写,到处运行”,但这个“到处”的前提是安装对应平台的JVM。Java本身的跨平台是靠不同平台的JVM实现来完成的,而不是说同一个JDK二进制能通吃所有CPU。所以拿到ARM设备后,第一件事就是确认CPU架构,这决定了你接下来搜索和下载什么后缀的包。

1.2 JDK 16在ARM平台上的特殊位置

Java 16是2021年3月16日发布的正式版本,它不属于LTS(长期支持)版本,下一个LTS是Java 17。那为什么单独聊JDK 16的ARM版?因为从Oracle JDK 9开始,官方对ARM的支持策略有过一次非常明显的变化:AArch64(也就是64位ARM)拿到了官方支持,但32位ARM的直接支持被逐步弱化。

到了Java 16,Oracle官方发布包里只有linux-aarch64,压根没有linux-arm32这个版本。如果你手里的设备是树莓派3B这类32位ARM板子,那官方JDK 16基本不用考虑了,只能去找第三方发行版或者自行编译。很多人在这一步容易卡住,因为他们默认“官网有的下载,ARM应该都支持”,实际上官方支持的ARM范围已经缩小到AArch64这一条线。

1.3 网上那些“ARM版JDK”到底怎么区分

我在搜索资料时看到有一条热搜词是“jdk arm 和aarch64选哪个”,这问题其实已经说到点子上了。ARM平台内部分成好几个子类,最常见的是:

  • AArch64:也叫ARM64、arm64,是64位ARM指令集,目前几乎所有新出的ARM处理器都是它,比如飞腾、鲲鹏、树莓派4及以后的型号。
  • armhf:32位ARM,硬浮点,常见于比较老的嵌入式板卡和部分入门级树莓派镜像。
  • armel:32位ARM,软浮点,性能弱,现在基本很少碰到。

选择逻辑很简单:64位ARM处理器,选aarch64版本;只有老旧的32位设备,才需要去找armhf版。而且即便都是ARM,32位和64位也是完全不同的二进制格式,不能混用。我这里建议优先选择aarch64,因为现代JDK在64位上的性能、内存寻址能力和默认GC表现都要好不少。

另外,国内一些Linux发行版会在自己的软件源里维护JDK,比如麒麟系统里直接apt install openjdk-16-jre就能装上。这类版本通常更贴近系统本身的库环境,安装确实省心,但版本可能滞后,而且不一定叫“JDK 16”,更多时候是跟着发行版自己的节奏走。

2. JDK16 ARM版完整安装流程

2.1 安装前先确认目标机架构,别急着下载

我记得自己第一次在ARM服务器上部署时,没有认真确认架构,下载了一个“看上去通用”的JDK,最后白白浪费了一个晚上。所以现在不管多急,我都会先执行这三条命令:

uname -m dpkg --print-architecture lscpu | grep Architecture

uname -m在64位ARM Linux上通常输出aarch64,在32位ARM上通常输出armv7l。dpkg --print-architecture在Debian系系统上会输出arm64或armhf。lscpu里的Architecture字段可以辅助确认。如果uname -m显示aarch64但dpkg显示armhf,说明系统是64位内核配了32位用户态,这种情况要按dpkg的输出去选版本。

这一步别偷懒。拿不准的时候,可以在终端里执行file /bin/ls,看返回里是AArch64还是ARM,那是更直观的判断方式。确认清楚了再决定下载哪个包,能省掉后面大量排错时间。

2.2 下载渠道与版本选择

JDK 16 ARM版的下载渠道,我实际用得比较多的是OpenJDK官方archive页面。Java 16的最终更新版本是16.0.2,建议直接下载这个版本,不要用EA早期体验版。下载时认准文件名的linux-aarch64后缀,类似jdk-16.0.2_linux-aarch64_bin.tar.gz。

如果你所在的环境不方便访问国外站点,可以先在内网一台可联网的机器上下载,再通过内网传输工具拷过去。这里只讨论正常的软件获取方式,不涉及任何额外的代理操作,目的就是拿到一个合适的安装包。

还有一类渠道是Azul Zulu、Adoptium Temurin这些第三方发行版。它们除了提供aarch64,还保留了armhf的包,对老设备很友好。页面筛选条件也比较直观,可以按操作系统、架构、版本逐项选。个人经验:能上官方JDK就上官方,需要32位ARM或者特殊商业支持的时候,再看Zulu和Temurin。

2.3 解压安装与环境变量配置

JDK本质上是绿色软件,解压即用,不需要像Windows那样跑安装向导。把下载好的tar.gz放到/opt目录下:

cd /opt sudo tar -zxvf jdk-16.0.2_linux-aarch64_bin.tar.gz

解压完成后会生成/opt/jdk-16.0.2目录。接下来配置环境变量。我喜欢在/etc/profile.d/下面新建一个jdk.sh文件,这样做的好处是所有用户登录时都会自动加载,不用单独改每个用户自己的.bashrc。

sudo vi /etc/profile.d/jdk.sh

写入以下内容:

export JAVA_HOME=/opt/jdk-16.0.2 export PATH=$JAVA_HOME/bin:$PATH

保存后执行:

source /etc/profile.d/jdk.sh java -version

如果输出OpenJDK 64-Bit Server VM的版本信息,就说明安装成功了。需要注意的是,有些系统可能已经有系统自带的Java,在配置PATH时,JAVA_HOME/bin要放在前面,这样最终执行的是我们新装的JDK。

2.4 离线环境怎么分发安装包

实际项目里经常会遇到目标机完全不能访问外网的场景,比如闭网的工控系统。这种时候离线安装JDK反而是最方便的,比在线装省事得多。因为JDK不依赖复杂的安装程序,只要把tar.gz文件拷贝到目标机,解压配置就行。

分发方式我常用的有两种:第一种是U盘拷贝,适合单台或者少量设备;第二种是搭建一个内网HTTP服务,把安装包放上去,目标机用wget或者curl拉取。不管哪种方式,复制完先执行md5sum或sha256sum校验一下包完整性,避免传输过程中文件损坏。JDK包损坏后会出现解压报错或者启动时缺少文件的诡异问题,校验能提前发现。

另外,离线环境下一定要预先确认目标机的glibc版本。JDK 16本身对系统的依赖不算低,如果目标机系统太老,后面会遇到GLIBC版本不足的报错,这个我在第4章专门展开讲。

3. 新装好的JDK16,这样验证才算真正跑起来

3.1 基础验证:java -version和HelloWorld

装好之后别急着部署业务,先用最基础的方式验证一下。也就是我们常用的:

java -version javac -version

直接在ARM机器上输入,观察输出。如果能看到java version "16.0.2"以及Java(TM) SE Runtime Environment字样,至少说明JVM能正常启动。接着可以写一个最简单的Java程序:

public class HelloArm { public static void main(String[] args) { System.out.println("Hello, JDK16 on ARM"); } }

编译和运行:

javac HelloArm.java java HelloArm

这一步是为了确认javac编译器也能正常工作。JDK和JRE不一样,如果只需要运行Java程序,装JRE就行,但要做开发或者编译,就得有完整的JDK。我自己习惯直接装JDK,省得以后写代码时缺少javac。

3.2 用系统属性确认运行架构

很多人看到java -version里显示64-Bit就放心了,其实还不够严谨。有些嵌入式环境是64位内核跑了32位用户态,这种情况下JVM也可能是32位的。为了彻底确认当前JVM运行的架构,可以写一段更小程序的代码,或者直接在JShell里执行:

jshell

然后输入:

System.getProperty("os.arch"); System.getProperty("sun.arch.data.model");

os.arch返回aarch64说明是64位ARM JVM,返回arm则说明是32位JVM;sun.arch.data.model返回64或32则直接告诉你位宽。这一步在排查容器部署问题时特别有用,因为容器里看到的架构不一定和宿主机一样。

3.3 试试JDK16的新语法

JDK 16虽然是个过渡版本,但引入的语法特性相当多,最典型的就是record、instanceof模式匹配、Stream.toList。这些特性在ARM平台上和在x86_64平台上行为完全一致,这也是Java跨平台能力的一种体现。可以在ARM机器上跑一下这段代码,验证JDK 16的新语法:

public class NewFeatures { record Point(int x, int y) {} public static void main(String[] args) { Object obj = new Point(3, 5); if (obj instanceof Point p) { System.out.println("Point: " + p.x() + ", " + p.y()); } var list = java.util.stream.Stream.of("a", "b", "c").toList(); System.out.println(list); } }

编译运行后,如果正常输出,说明JDK16的完整编译器在ARM上没有任何缩水。这一点对有“同一个Jar包同时跑在不同架构服务器上”需求的团队影响很大:用JDK16的新语法写代码,在ARM上编译出的class和x86上编译出的class,JVM都能正确加载运行。

3.4 启动参数和实际调优

ARM设备的内存配置通常比x86服务器紧张,JVM启动参数需要更谨慎一些。以我常用的一个Java服务为例:

java -Xms256m -Xmx1g -XX:+UseG1GC -jar myapp.jar

-Xms设置堆初始大小,-Xmx设置堆最大值,-XX:+UseG1GC指定G1垃圾收集器。JDK 16默认就是G1,在大多数场景下不用额外指定。如果设备内存确实有限,可以把-Xmx压到512m,同时观察Full GC频率,别让堆太小导致频繁GC拖垮吞吐。

如果你手头的机器内存比较大,比如开发板配了8G以上内存,可以尝试ZGC。不过JDK16里ZGC还是实验特性,需要解锁参数才能使用,生产环境使用前一定要经过充分测试。我一般在嵌入式设备上不推荐ZGC,G1在低内存场景下表现更稳定。

4. 安装与使用过程中踩过的坑

4.1 Exec format error,八成是架构不匹配

最典型的报错长这样:

bash: ./java: cannot execute binary file: Exec format error

这个报错第一次遇到时很容易懵,但排查思路其实很清晰:JDK二进制文件和当前系统CPU指令集不匹配。要么是你在ARM机器上下了x86_64包,要么是你下载了32位ARM包,但内核或用户态是64位。遇到这个报错,直接回到第2章重新确认架构,多花两分钟比瞎折腾一小时强。

另外还要注意,有些机器表面上是ARM处理器,但系统是32位用户态,你下载aarch64的JDK也会报Exec format error。反过来,64位系统通常能运行armhf的32位程序,如果系统配置了兼容库的话。这就是为什么要确认dpkg --print-architecture和uname -m两个结果的原因。

4.2 GLIBC版本不够,JDK版本和系统库脱节

另一个高频报错是:

./java: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./java)

这个报错说明系统自带的glibc版本低于JDK 16编译时候的要求。JDK 16是2021年的产物,对系统库的版本要求并不低,如果目标机还是CentOS 7或者更老的Ubuntu,直接跑JDK16就会遇到这个坑。

解决办法有三个:第一,升级整个操作系统到新版本,这是最彻底的方式;第二,换用对系统依赖更低的JDK版本,比如JDK 8或JDK 11;第三,手动升级glibc,但这是高风险操作,强烈不建议在生产环境直接搞,很可能把系统搞挂。我在实际项目中遇到老系统时,更倾向于换JDK版本而不是硬上JDK16。

4.3 32位ARM设备,JDK16基本没戏

如果你的目标机是armv7l,并且系统是32位,那前面也提到了,官方JDK 16没有armhf的包。这种情况下,有两条现实路线:

  • 降级到JDK 8的ARM版本,老系统配合老JDK,兼容性最好。
  • 改用Azul Zulu或Adoptium Temurin提供的armhf版本,它们在较新版本上还保留了32位ARM支持。

至于自己下载OpenJDK源码去交叉编译,这个过程非常繁琐,需要准备交叉编译工具链、处理大量依赖问题、还要理解OpenJDK的构建系统。我不太建议第一次接触ARM的人尝试,除非你真的需要JDK 16的某个新特性,且设备完全没有升级空间。

4.4 和JDK17如何选择?怎么选不踩雷

现在网上搜“jdk17 arm下载地址”的人很多,这也说明大家其实都在关心长期支持版本。我的建议是:新项目直接上JDK 17 ARM版,因为17是LTS,能获得更长时间的安全更新和维护。但如果你的业务框架锁定了JDK 16,或者只是临时验证某个技术方案,那JDK16 ARM版也完全可以跑,只是要接受它已经过维护期的事实。

实际部署时,我习惯把JAVA_HOME做成软链来管理:

sudo ln -s /opt/jdk-16.0.2 /opt/jdk

这样以后换JDK版本时,只需要修改软链指向,不需要改动任何脚本和服务配置。这个方法在很多生产环境里都用得上,可以避免因为版本切换导致的乱七八糟的路径问题。

根据我自己的实际操作体会,在ARM设备上折腾JDK16,最核心的一件事就是把架构确认清楚。不管是飞腾、鲲鹏还是树莓派,只要架构识别对了,JDK16在ARM上的体验和x86_64上几乎没差别。很多看起来诡异的报错,到最后都指向同一个问题:下载的包和系统不匹配。如果你也准备在ARM设备上部署Java服务,建议别急着下载,先花两分钟把目标机的架构、glibc版本、系统位数都确认好,后面真的会顺利很多。

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

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

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

立即咨询