引言
最近打算开始读《深入理解Java虚拟机》,作者在第一章推荐读者自己编译一份JDK,并使用CLion来阅读和调试源码。由于笔者没有原生的Linux或MacOS环境,只能使用WSL2来平替。在配置过程中踩了一些坑,遂记录一下,没准以后会有人需要。
获取源码
源码的仓库在https://hg.openjdk.java.net/jdk/jdk12 ,点击左边的browse就是源码的文件目录了。本来想直接在WSL里使用wget来下载.tar.gz文件,发现下载不动,于是选择使用Windows的浏览器直接下载.tar.gz,再从文件系统里找到WSL的目录复制进去。
然后从WSL里打开对应目录就能找到源码文件,虽然多了一个不知道有什么用的Identifier(猜测跟Windows的文件系统有关),但是并不影响源码的解压。使用tar指令解压压缩包即可获得源码。
1 | tar -xzvf jdk12-06222165c35f.tar.gz |
构建编译环境
不得不说Linux下配环境确实比Windows方便不少。
在开始之前建议先更新一下软件源:sudo apt-get update
安装GCC
1 | sudo apt-get install build-essential |
安装一些依赖库
这个表是书的作者提供的,实际操作的时候发现我的环境还缺了一个ZIPEXE(提示Could not find required tool for ZIPEXE
),安装后就好了:sudo apt-get install zip
安装Bootstrap JDK
编译大版本号为N的JDK时需要用到另一个大版本号至少为N-1的已经编译好的JDK。因为OpenJDK只有部分代码使用C/C++编写,更多的是使用Java编写的,所以需要另一个编译期可用的JDK来编译这部分代码,官方称这个JDK为“Bootstrap JDK”
1 | sudo apt-get install openjdk-11-jdk |
进行编译
源码目录中有一个叫configure
的bash文件,提供检查依赖项是否齐全和设置参数等功能。作者给出的配置指令是bash configure --enable-debug --with-jvm-variants=server
,配置完毕后使用make images
进行编译。但是实际上我遇到了两个问题。
没有jre目录
报错信息如下:
1 | 错误的路径元素 "/usr/lib/jvm/java-17-openjdk-amd64/jre/lib": 没有这种文件或目录 |
我顺着这个目录找下去,发现我只有/usr/lib/jvm/java-17-openjdk-amd64
目录,因为从JDK11开始jre已经不是默认安装了。我们可以手动生成一个(需要切换到需要生成jre的JDK的目录):
1 | ./bin/jlink --module-path jmods --add-modules java.desktop --output jre |
这里需要调用的应该是上面提到的Bootstrap JDK,但是我环境变量之前配过JDK17,所以这里就调用了JDK17,而不是新下载的JDK11。
调用strncpy产生的奇怪问题
报错信息如下:
1 | === Output from failing command(s) repeated here === |
说实话没怎么看懂,似乎和什么字符串被截断有关,在网上查了很久都没找到有类似情况的。然后发现官方有在源码目录里附上docs,于是不抱希望地点开查看,在building.html
的Troubleshooting
里找到了这样一句:
1 | Hint: If caused by a warning, try configure --disable-warnings-as-errors |
又想到报错信息里有这么一句:
1 | cc1plus: all warnings being treated as errors |
于是清除原来的配置之后重新配置,添加不把warnings视作errors的配置:
1 | make clean |
之后再make images
就编译成功了。进入build/配置名称/jdk/bin
目录下执行./java -version
可以看到版本信息:
1 | openjdk version "12-internal" 2019-03-19 |
在CLion中进行源码调试
由于不是原生的Linux环境,我在尝试从源码导入CMake项目的时候也卡了很久,最后按照这篇文章的流程完成了配置。
CLion连接Ubuntu
打开CLion,左侧选择WSL,再选择New Project,然后根据指引操作。选择文件目录时选择刚才的JDK源码目录(即解压之后得到的目录)。之后CLion会自动开始在WSL里下载服务端并启动。
创建自定义Build Target
点击IDE右上角的齿轮,选择Settings,左边找到Custom Build Targets,点击“+”新建一个Build Target,Toolchain选择Default,并填写Name。
然后点击Build或Clean右边的“…”,新建两个External Tools:
新建完后把make填到Build里,make clean填到Clean里。
创建自定义Run/Debug configuration
点击这个位置的Edit Configurations,并新建一个Custom Build Application。
Target选择刚才自定义的Build Target,Executable选择编译出来的JDK(刚才查看version时执行的那个程序),Program arguments可以先填一个-version测试一下。下面的build记得删掉。
至此配置完成。
测试运行、打断点
完成以上配置之后,右上角的Run和Debug应该已经变成绿色的了。
点击Run,可以发现输出了JDK的版本信息。
接下来测试断点功能。JVM的入口函数是src/java.base/share/native/libjli/java.c
文件下的JavaMain(void * _args)
函数。在这个函数打上断点后点击右上角的Debug,程序就会在断点处停止运行,并看到我们传入的参数信息。
有的没的
Long time no see
又有快一年没更新了。去年暑假在上海实习,回来之后彻底从e人变成了i人,没事就喜欢宅着。顺利拿了转正offer,人生的下一个阶段就是在上海当牛马了。现在又开始觉得自己没有什么可以失去的了,孩子我无敌了。上个学期一直在摆烂,最近肝完了毕业论文的初稿,趁着还算是有一点点学习的状态学一点以后工作可能用得上的东西。希望这段时间能经常更新吧。