我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Java 16 ARM版JDK安装与交叉编译实战指南

Java 16 ARM版JDK安装与交叉编译实战指南 简介这是一份适用于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标识有这三种aarch6464位ARM架构对应ARMv8-A及以上的64位指令集。现在绝大多数服务器级ARM芯片、树莓派4B/5、飞腾、鲲鹏、Apple Silicon的Linux虚拟机都是aarch64。armhf32位ARM架构但要求硬件支持硬浮点ARMv7及以上。树莓派2/3的32位系统、大部分老款Android开发板的Linux系统走的是armhf。armel32位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等但通常更偏向更高版本的JDKJDK 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?projectjdk返回的JSON里会有完整的下载链接、SHA256校验值、文件大小等信息。注意Adoptium的API支持/latest/16/这种写法但如果你需要精确到某个构建版本可以把latest换成具体的版本号比如16.0.27。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.27这样的格式做一层软链方便后续版本切换sudo ln -s /usr/local/java/jdk-16.0.27 /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.27-7) OpenJDK 64-Bit Server VM (build 16.0.27-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版本更新节奏比较慢且不一定是最新安全更新适合学习环境生产环境我还是推荐手动解压官方包至少你能精确掌控补丁版本。容器化是我个人比较推荐的方式尤其是在云边协同场景中。参考一下这个DockerfileFROM 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字节码文件然后把JREJDK的运行时部分放到ARM设备上Copy整个应用过去就能跑。字节码是平台无关的不用交叉编译。第二种场景你需要在x86开发机上构建出能在ARM上运行的OpenJDK本身。这才是真正的交叉编译需要配置完整的交叉编译工具链过程复杂得多。大多数普通开发者的需求是第一种完全不需要交叉编译。JDK 16 ARM版在这个场景中的角色是“运行时”而不是“编译工具链”。所以如果你只是写业务代码就不要被“arm交叉编译”这个搜索词带偏了。5.2 JDK 16在x86主机上为ARM目标构建应用你的开发机是x86_64目标设备是ARM64构建流程应该是这样开发机安装任意平台的JDK 16x86_64就行。用Maven或Gradle正常构建产出可执行的JAR包。把JAR包和对应ARM架构的JRE一起打包分发到目标设备。目标设备解压后直接java -jar运行。这里要提醒的是如果你用到了JNIJava 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_DIRdist-arm64 JDK_TGZjdk-16-arm64.tar.gz APP_JARmyapp.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-targetaarch64-linux-gnu \ --with-jvm-variantsserver \ --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 运行环境类问题问题1cannot execute binary file: Exec format error这个报错最常见的原因就是架构不匹配比如在aarch64系统上执行了x86_64的JDK二进制。解决方式运行uname -m确认系统架构然后重新下载对应架构的JDK。问题2libjli.so: cannot open shared object file这类问题一般是因为JDK解压后目录结构被改动过或者环境变量指向了错误的路径。检查一下JAVA_HOME是否指向了JDK的完整根目录不要指向bin目录。问题3Error: Could not create the Java Virtual Machine优先排查内存配置。ARM小设备上默认的堆内存策略可能不合适可以手动加上-Xmx256m这样的参数限制堆大小。我曾在128MB内存的设备上遇到过类似问题把堆大小限制到64MB后就能跑起来了。6.2 JDK版本和系统库交互问题问题4Error: dl failure on line 893这个错误在嵌入式Linux上遇到得比较多跟JDK加载本地库时依赖的libz等系统库有关。排查步骤ldd $JAVA_HOME/bin/java看输出里有没有not found的行。如果有就是系统缺少对应的共享库比如libz.so.1用apt install zlib1g或yum 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.jfrJDK 16里JFRJava 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它在单核低内存设备上的表现经常会让你意外。本文还有配套的精品资源点击获取
返回列表