实战:自己编译JDK
参考周志明老师的《深入理解Java虚拟机》第3版的第一章,自己动手实践了一下编译JDK
1.获取源码
选用OpenJDK12来编译,打开jdk版本仓库页面http://hg.openjdk.java.net/jdk/jdk12/,点击左边菜单中的"Browse"将显示源码根目录页面。
点击左侧的"zip"链接即可下载当前版本打包好的源码。
2.系统需求
尽量在Linux或MacOS上构建OpenJDK,这两个系统在准备构建工具链和依赖项上要比在Windows或Solaris平台上要容易许多。本次实践将以Ubuntu 18.04.05 LTS为平台进行构建。
选择64位桌面版
本次编译采用64位操作系统,默认参数下编译出来的也是64位的OpenJDK,如果需要编译32位版本,同样推荐在64位的操作系统上进行,理由是编译过程可以使用更大内存(32位系统受4G内存限制),通过编译参数(–with-target-bits=32)来指定需要生成32位编译结果即可。在官方文档上要求编译OpenJDK至少需要24G的内存空间,而且至少要68GB的空闲磁盘空间。
安装Ubuntu教程
3.构建编译环境
Ubuntu里用户可以自行选择安装GCC或CLang来进行编译,但必须保证最低的版本为GCC 4.8或者CLang 3.2以上,官方推荐使用GCC 7.8或者CLang 9.1来完成编译。在Ubuntu系统上安装GCC的命令为:
sudo apt-get install build-essential
在编译过程中需要依赖FreeType、CUPS等若干第三方库,OpenJDK全部的依赖库已在下表列出,可执行相应的安装命令来完成安装。
工具 | 库名称 | 安装命令 |
FreeType | The FreeType Project | sudo apt-get install libfreetype6-dev |
CUPS | Common UNIX Printing System | sudo apt-get install libcups2-dev |
X11 | X Window System | sudo apt-get install libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev |
ALSA | Advanced Linux Sound Architecture | sudo apt-get install libasound2-dev |
libffi | Portable Foreign Function Interface Library | sudo apt-get install libffi-dev |
Autoconf | Extensible Package of M4 Macros | sudo apt-get install autoconf |
编译大版本号为N的JDK,我们还要另外准备一个大版本号至少为N-1的、已经编译好的JDK。编译OpenJDK 12时,Bootstrap JDK必须要使用JDK11及之后的版本。在Ubuntu中使用以下命令安装OpenJDK 11:
sudo apt-get install openjdk-11-jdk
4.进行编译
需要下载的编译环境和依赖项目都齐备后,我们就可以按照默认配置来开始编译了。但通常我们编译OpenJDK的目的都不仅仅是为了得到在自己机器中诞生的编译成品,而是带着调试、定制化等需求,这样就必须了解OpenJDK提供的编译参数才行,这些参数可以使用“bash configure --help"命令查询到。
所有参数均通过以下形式使用:
bash configure [options]
譬如,编译FastDebug版、仅含Serve模式的HotSpot虚拟机,命令应为:
bash configure --enable-debug --with-jvm-variants=server
如果编译过程中需要的工具类或者依赖项有缺失,命令执行后将会得到明确的提示,并且给出该依赖的安装命令,如我就缺少了libfontconfig1-dev,感觉是作者故意没说。
一切顺利就会收到配置成功的提示,并且输出调试级别,Java虚拟机的模式、特性,使用的编译器版本等配置摘要信息,如下所示。
依赖检测通过后便可以输入“make”执行整个OpenJDK编译了。
如果电脑配置不高的话现在就可以去开始一把游戏或者看两集电视剧了,我只给虚拟机分配了一个核心和4GB内存,编译花了差不多40分钟。
编译完成如下所示
编译完成后进入OpenJDK源码的“build/配置名称/jdk"就可以看到OpenJDK的完整编译结果了。
编译完成后”build/配置名称“目录下结构如下图所示:
如果多次编译,或者目录结构成功产生后又再次修改了配置,必须先使用“make clean"和”make dist-clean"命令清理目录,才能确保新的配置生效。
“build/配置名称/jdk"下目录结构如下图所示:
进入“build/配置名称/jdk/bin"目录,输入以下命令验证jdk版本。
./java -version
输入如下,宣告成功。
5.在IDE工具中进行源码调试
本次实战中,这里建议使用的IDE是JetBrian的CLion 2020.1.3及之前的版本,可以在JetBrian官网下载并免费使用30天,学生可以验证免费使用。我原本下载的是最新的CLion 2021.2,但新版本没有“New CMake Project from Sources”,我搞了半天也没能生成CMakeLists.txt,放弃了,下面是官网的说明,可以参考一下,反正我是行不通。Clion官网的说明是打开CMake项目的根目录,打开一个源文件在编辑器中,如果在顶层目录中没有CMakeLists.txt文件,CLion会建议创建一个。有弄出来的小伙伴可以指导一下。
CLion安装后,新建一个项目,选择“New CMake Project from Sources”,在源码文件夹中填入OpenJDK源码根目录,此时CLion已经自动选择好了需要导入的源码,如下图所示。点击OK按钮就会自动导入源码并自动创建好CMakeLists.txt文件。
这份自动生成的CMakeLists.txt并不能直接使用,OpenJDK本身也没有为任何IDE提供支持,但如果只是为了能够在CLion中追踪、阅读源码,而不需要修改重新编译的话,直接在Run/Debug Configuration中增加一个CMake Application,然后Executable选择我们刚才编译出来的FastDebug的java命令,运行参数加上-version或者某个Class文件的路径,再把Before launch里面的Build去掉,就可以开始运行调试了,如下图所示。
运行结果如下图所示。
HotSpot虚拟机启动器的执行入口是java.c的JavaMain()方法,可以设置断点单步跟踪,如下图所示。
花了差不多三个下午终于做完了,中间遇到了不少困难,总算还是做完了。