1. 为什么需要自己编译JDK17?
在开始之前,我们先聊聊为什么要自己编译JDK。虽然Oracle和各大厂商都提供了预编译好的JDK二进制包,但自己动手编译有几个不可替代的优势:
- 深度定制:你可以根据需求启用/禁用特定功能模块,比如移除不需要的模块减小体积
- 性能调优:针对特定硬件架构进行优化编译,比如针对ARM处理器优化
- 学习原理:这是理解JVM底层工作机制的最佳实践方式
- 特殊需求:比如需要修改HotSpot源码或添加调试信息
我最近在为一个嵌入式项目定制轻量级Java运行时,就不得不自己编译JDK。过程中积累了不少经验,今天就来分享这个"从源码到可执行JDK"的完整过程。
2. 环境准备与工具链配置
2.1 硬件要求
编译JDK17是个资源密集型任务,建议准备:
- CPU:至少4核(8线程以上更佳)
- 内存:16GB起步(32GB更稳妥)
- 磁盘:SSD硬盘,预留30GB空间
实测数据:在Ryzen 7 5800X(8核16线程)+32GB内存的机器上,完整编译耗时约35分钟。如果配置不足,编译可能耗时数小时。
2.2 操作系统选择
官方支持以下平台:
- Linux(推荐Ubuntu 20.04 LTS)
- macOS 10.13+
- Windows 10(需WSL2)
我个人推荐使用Linux环境,因为:
- 依赖管理更方便
- 编译速度更快
- 问题排查资料更丰富
2.3 必要工具安装
以Ubuntu为例,执行以下命令安装基础工具链:
sudo apt update sudo apt install -y git build-essential libx11-dev libxext-dev libxrender-dev \ libxtst-dev libxt-dev libcups2-dev libfontconfig1-dev libasound2-dev \ libfreetype6-dev autoconf zip unzip关键工具说明:
build-essential:包含gcc/g++等编译工具autoconf:配置脚本生成工具lib*dev:各种图形/音频相关的开发库
3. 获取JDK17源码
3.1 源码仓库选择
JDK源码主要有两个来源:
- OpenJDK官方仓库(推荐)
- 厂商定制版本(如Adoptium)
我们使用OpenJDK官方源码:
git clone https://github.com/openjdk/jdk17u.git cd jdk17u小技巧:如果网络不稳定,可以使用国内镜像源:
git clone https://gitee.com/mirrors/jdk17u.git
3.2 源码目录结构解析
主要目录说明:
hotspot/:JVM核心实现java.base/:Java基础类库make/:构建系统配置test/:测试套件
了解目录结构有助于后续的定制修改。
4. 配置与编译过程
4.1 引导JDK准备
编译JDK需要一个已有的JDK作为"引导",这个引导JDK的版本必须比要编译的版本低1-2个版本。对于JDK17,我们可以用JDK16或JDK15。
下载并配置引导JDK:
wget https://download.java.net/java/GA/jdk16/7863447f0ab643c585b9bdebf67c69db/36/GPL/openjdk-16_linux-x64_bin.tar.gz tar -xzf openjdk-16_linux-x64_bin.tar.gz export PATH=$PWD/jdk-16/bin:$PATH验证引导JDK:
java -version # 应该显示JDK16的版本信息4.2 生成配置脚本
运行自动配置:
bash configure --enable-debug --with-jvm-variants=server \ --with-native-debug-symbols=internal --with-target-bits=64关键参数说明:
--enable-debug:生成调试信息--with-jvm-variants=server:编译server版JVM--with-native-debug-symbols=internal:保留符号表--with-target-bits=64:64位架构
4.3 开始编译
执行完整构建:
make images这个命令会:
- 编译所有模块
- 生成完整的JDK镜像
- 打包成可分发格式
编译过程会产生大量中间文件,确保磁盘空间充足。如果遇到内存不足,可以尝试:
make JOBS=4 images限制并行编译任务数
5. 验证与安装
5.1 编译结果检查
编译完成后,生成的JDK位于:
cd build/linux-x86_64-server-release/images/jdk bin/java -version应该看到类似输出:
openjdk version "17-internal" 2021-09-14 OpenJDK Runtime Environment (build 17-internal+0-adhoc..jdk17u) OpenJDK 64-Bit Server VM (build 17-internal+0-adhoc..jdk17u, mixed mode)5.2 安装到系统路径
建议将编译好的JDK链接到标准位置:
sudo ln -s $PWD /usr/local/java/jdk17-custom然后更新环境变量(添加到~/.bashrc):
export JAVA_HOME=/usr/local/java/jdk17-custom export PATH=$JAVA_HOME/bin:$PATH6. 高级定制技巧
6.1 模块化编译
JDK17采用模块化设计,可以只编译特定模块:
make jdk.jdi常用模块:
java.base:基础模块jdk.compiler:javac编译器jdk.jlink:jlink工具
6.2 调试符号处理
如果需要调试HotSpot,可以这样生成完整符号:
make images OBJCOPY=: STRIP_POLICY=no_strip这样生成的二进制会包含完整调试信息,方便用gdb调试。
6.3 交叉编译
在x86上编译ARM版JDK:
bash configure --openjdk-target=aarch64-linux-gnu \ --with-toolchain-path=/path/to/aarch64/toolchain需要提前准备好交叉编译工具链。
7. 常见问题排查
7.1 内存不足错误
症状:
g++: internal compiler error: Killed (program cc1plus)解决方案:
- 增加swap空间
- 减少并行编译任务:
make JOBS=2
7.2 依赖缺失错误
症状:
configure: error: Could not find freetype!解决方案:
sudo apt install libfreetype6-dev7.3 版本冲突
症状:
error: invalid suffix on literal这通常是gcc版本太旧导致,需要升级到gcc-9+:
sudo apt install gcc-9 g++-9 sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 908. 性能优化建议
8.1 编译参数调优
在configure时添加这些参数可以提升生成代码的性能:
--with-extra-cflags="-O3 -march=native" \ --with-extra-cxxflags="-O3 -march=native"8.2 链接时优化
使用LTO优化:
--enable-link-time-optimization需要gcc 9+支持,可以提升5-10%性能。
8.3 裁剪无用模块
通过--with-jvm-features参数禁用不需要的功能:
--with-jvm-features=-dtrace,-cds9. 实际应用案例
9.1 嵌入式场景定制
为树莓派定制最小化JDK:
bash configure --with-jvm-variants=minimal \ --disable-warnings-as-errors --with-jvm-features=-dtrace,-cds \ --with-extra-cflags="-Os -mcpu=cortex-a72" \ --openjdk-target=armv7l-linux-gnueabihf9.2 添加调试信息
开发JVM插件时的调试配置:
make CONF=linux-x86_64-server-fastdebug会生成带有调试符号的fastdebug版本。
10. 后续维护建议
10.1 增量编译
修改源码后,只需重新运行:
make images构建系统会自动检测变更部分。
10.2 清理构建
完全重新构建:
make clean make images10.3 版本管理
建议将定制修改提交到独立分支:
git checkout -b my-custom-jdk git commit -am "Add my custom patches"这样下次可以从你的分支直接开始编译。