最近开始学习 JVM,跟着『深入理解 Java 虚拟机』一起趟坑,记下这篇笔记。以下内容都是在 64 位的 Ubuntu 18.04 的环境中进行操作的结果。由于书中写的是 OpenJDK 7 的相关内容,所以先熟悉书中相应版本的内容,之后再研究新版本的特性。
前期准备
首先当然是获取 OpenJDK 的源码,获取源码有两种方式:第一种是直接从官网下载打包好的源码,下载完解压到本地就可以了,但是,这种方式下载,不知道是我没找对地方还是官网真的改的一言难尽,很多超链接都 404 了,所以这种方式不是很推荐,这里还是放上我找到的下载地址;另一种方式就是版本控制工具下载,不过这个版本控制工具不是 Git、SVN 这类比较常见的,而是 Mercurial。以下命令可以安装 Mercurial:
1 | sudo apt-get install mercurial |
安装好 Mercurial 就可以通过 OpenJDK 的仓库地址将源码拉下来了:
1 | hg clone http://hg.openjdk.java.net/jdk7/jdk7 OpenJDKDirName |
其他版本的仓库地址官方文档中 Documentation 部分找到,下载过程可能会有点慢,等着就好了,下载完了一定要打开 OpenJDKDirName/README-builds.html
看看,或者上述 Documentation 中各个版本的 README
看看,内容是一样的,这份文档详细的介绍了编译 OpenJDK 需要注意的事项以及编译的步骤。编译 OpenJDK 需要注意的依赖要求及我在配置过程遇到的坑记录如下:
Bootstrap JDK: 这个 JDK 是指上一个 release 版本的 JDK 作为基础,比如编译 OpenJDK 7 就需要 OpenJDK 6,并且需要将 OpenJDK 的路径配置为
ALT_BOOTDIR
的值Ant: Ant 需要 1.7.1 以上的版本。这个需要注意不能使用 1.10.x 以上的版本,因为 1.10.x 以上的版本需要 JDK 8 的支持,所以这个建议到Ant官网手动下载合适的版本,或者直接从
README-builds
提供的链接下载,手动安装需要配置环境变量ANT_HOME
为 Ant 的路径,并且将${ANT_HOME}/bin
配置到PATH
中FreeType 2: 需要 2.3 以上的版本。这里遇到个奇怪的 bug,我安装了 2.10 版本的 FreeType 2,最后检查的时候非说 2.10 版本低于 2.3,无奈最后从官网下载了 2.9 配置好才没有报错。从 FreeType 2 官网下载好源码好按照其中的
README
文档进行编译安装,最终库文件位于/usr/local/lib
下,对应需要配置的变量为ALT_FREETYPE_LIB_PATH
;头文件位于/usr/local/include
下,对应需要配置的变量为ALT_FREETYPE_HEADERS_PATH
ALSA: 这个依赖通过
sudo apt-get install libasound2-dev
来安装即可
依赖安装完成后对应需要配置环境变量,除了以上各依赖对应的环境变量以外,参考书中内容整理了配置环境变量的脚本 variable_script.sh
:
1 |
|
编译之前运行 source variable_script.sh
即可让以上环境变量生效,之后使用 make sanity
检测变量配置情况,如果最终出现 Sanity check passed
则表示检测通过了,可以输入 make
命令进行编译了,如果出现错误提示,根据提示解决相应问题就行了。当然这些命令都是在 OpenJDKDirName
目录下运行的。最终会在终端看到日志输出,如果需要保存日志的话,执行 make
命令的时候利用管道将日志输出到文件即可,将 make
命令换成 make 2>&1 | tee <destination>/build.log
。整个编译过程大概持续了半小时,静静等着就好了,如果中途出错,它会提示你的。
编译过程遇到的坑
由于编译过程没有记录日志,印象中除了依赖部分提到的问题,另外遇到的坑就是编译过程中报出 cc1plus all warnings being treated as errors ubuntu
,这是因为编译检测过程比较严格,将警告当成错误处理阻止继续编译,解决办法是修改配置文件 ./hotspot/make/linux/makefiles/gcc.make
,将 WARNING_ARE_ERRORS
变量改为
1 | WARNING_ARE_ERRORS = -Wno-error |
编译结束后,如果看到类似以下的信息,说明 OpenJDK 编译成功了。
1 | #-- Build times ---------- |
编译成功后进入编译结果存放的路径,下面有 ./j2sdk-image
进入后就是平时使用的 JDK 了,将其配置到 JAVA_HOME
环境变量就可以正常使用 JDK 了。使用 java -version
还可以看到用户名的机器名,比如我的就是:
1 | openjdk version "1.7.0-internal" |
至此,OpenJDK 的编译就告一段落了。既然我们是研究 JVM,当然我们的主要目标是 Hotspot,而且调试虚拟机后的改动也不用编译整个 OpenJDK 了,那继续单独编译 Hotspot。
单独编译 Hotspot 虚拟机
编译虚拟机其实也很简单,不过书中写的不是特别明了,所以还是费了我不少时间,这里就简单记录下完整的过程就好了。
很明显,Hotspot 是由 C 编写的,由 Makefile 管理编译过程,所以可以将 Hotspot 作为单独模块拿出来编译,使用的就是 OpenJDKDirName/hotspot/make
目录下的 Makefile
文件,记得终端需要切换到 OpenJDKDirName/hotspot/make
目录下,其他参数与编译 OpenJDK 时保持一致就 ok,如果需要重新配置环境变量的话,使用之前编写的 source variable_script.sh
就可以了。
1 | cd OpenJDKDirName/hotspot/make |
期间可能会因为 Bootstrap JDK 是 64 位的终端编译,查看日志会发现提示 JAVA_HOME must point to 32 bit
之类的,解决方法就是下载一个 32 位的 Bootstrap JDK 并设置好环境变量 ALT_BOOTDIR
即可。
编译过程大概也是半小时左右,耐心等待就好了,输出结果存放在之前配置的结果存放路径下的 ./hotspot/outputdit/bsd_compiler2
目录中,由于没有制定编译版本,所以默认是 product,进入 product
下修改下 env.sh
文件,新增一个环境变量如下:
1 | LD_LIBRARY_PATH=.:${JAVA_HOME}/jre/ilb/amd64/native_threads:${JAVA_HOME}/jre/lib/amd64 |
配置完后在当前路径执行以下命令启动虚拟机,输出版本号:
1 | . ./env.sh |
会看到如下结果:
1 | Using java runtime at: /home/crazypudding/Documents/jdk7u/build/linux-amd64/j2sdk-image/jre |
至此,Hotspot 虚拟机也完成编译了~