假期得闲,想着最近Java 9已发布,要不来编译一下OpenJDK 9吧。
说干就干,首先就是获取源码。OpenJDK的源码使用mercurial管理,所以没有安装过mercurial的话需要先安装mercurial,使用brew的话,可以直接用brew进行安装。
brew install mercurial
安装完mercurial后就可以获取源码了,mercurial的命令是hg。
hg clone http://hg.openjdk.java.net/jdk9/jdk9 jdk9
cd jdk9
bash ./get_source.sh
get_source.sh这个脚本用于辅助获取jdk9相关的所有子项目(包括corba、jdk、jaxp、jaxws、hotspot、nashorn等)。
jdk9使用autotools生成Makefile,支持多种工具链(如gcc、xlc、clang),由于我自己的开发机是mac,所以在此就介绍如何使用clang工具链来进行编译。
cd jdk9
chmod u+x configure
./configure --enable-debug --with-target-bits=64 --with-jvm-variants=server --disable-warnings-as-errors --with-toolchain-type=clang
# --enable-debug 用于开启调试功能
# --with-target-bits=64 用于指定基于64位进行编译
# --with-jvm-variants=server 用于指定只编译server版本的jdk
# --disable-warnings-as-errors 是为了编译通过不要把警告当错误处理
# --with-toolchain-type=clang 则是指定编译用的工具链为clang
执行以上命令后就会生成编译jdk9项目使用的相关文件(Makefile和make目录),此时执行make就可以进行编译了,但编译的时候可能会遇到一些错误,我遇到的错误主要是关于指针和零值比较的。我遇到过3处报错,可供参考,我的修改方式也较为简单,只是把比较去掉,直接判断是否为空指针。
jdk9/hotspot/src/share/vm/opto/lcm.cpp:42:35: error: ordered comparison between pointer and zero ('address' (aka 'unsigned char *') and 'int')
if (Universe::narrow_oop_base() > 0) { // Implies UseCompressedOops.
~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~
jdk9/hotspot/src/share/vm/opto/loopPredicate.cpp:903:73: error: ordered comparison between pointer and zero ('const TypeInt *' and 'int')
assert(rng->Opcode() == Op_LoadRange || _igvn.type(rng)->is_int() >= 0, "must be");
~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~
jdk9/hotspot/src/share/vm/memory/virtualspace.cpp:584:14: error: ordered comparison between pointer and zero ('char *' and 'int')
if (base() > 0) {
~~~~~~ ^ ~
执行make命令进行编译。
make
如果顺利的话最终会看到类似的输出。
Finished building target 'default (exploded-image)' in configuration 'macosx-x86_64-normal-server-fastdebug'
编译完后的输出文件在build目录下,因为只编译了server版本,所以输出的目录是build/macosx-x86_64-normal-server-fastdebug。这个目录下的hotspot和jdk就是我们想要的东西了。其中hotspot目录中含有一个名为hotspot的脚本,用于辅助调试jvm,先尝试执行一下hotspot脚本。
cd hotspot/variant-server/libjvm
bash hotspot
我得到的是类似下面的输出
Error: missing `/xxx/jvm/jdk9/build/macosx-x86_64-normal-server-fastdebug/hotspot/variant-server/libjvm' JVM at `/xxx/jvm/jdk9/build/macosx-x86_64-normal-server-fastdebug/hotspot/variant-server/libjvm/libjvm.dylib'.
问题也比较明确,缺少了libjvm.dylib这个文件来支持jvm的启动。那就打开hotspot脚本来看一下问题出在哪。其中有一段这样的内容
REL_MYDIR=`dirname $0`
MYDIR=`cd $REL_MYDIR && pwd`
...
JPARMS="-XXaltjvm=$MYDIR -Dsun.java.launcher.is_altjvm=true"
缺啥补啥,既然缺libjvm.dylib,那我们就来搜一下生成的目录下有没有这个文件,在build/macosx-x86_64-normal-server-fastdebug目录下进行查找。
find . -name 'libjvm.dylib'
我得到的输出如下。
./hotspot/variant-server/libjvm/gtest/libjvm.dylib
./jdk/lib/server/libjvm.dylib
./jdk/lib/server/libjvm.dylib.dSYM/Contents/Resources/DWARF/libjvm.dylib
./support/modules_libs/java.base/server/libjvm.dylib
./support/modules_libs/java.base/server/libjvm.dylib.dSYM/Contents/Resources/DWARF/libjvm.dylib
最简单的方式就是把gtest目录下的libjvm.dylib复制到libjvm目录下(注意:这几个libjvm.dylib的功能并不一致,md5校验也不同,此处只是为了让hotspot脚本运行起来)。
cp hotspot/variant-server/libjvm/gtest/libjvm.dylib hotspot/variant-server/libjvm/
由于之前编译使用的clang工具链,所以调试也就基于lldb进行,但原来的hotspot脚本中并没有lldb调试的相关内容,需要进行添加。修改hotspot脚本
...
# 约在85行左右,此处添加对lldb参数对识别
case "$1" in
-gdb)
MODE=gdb
shift
;;
-lldb)
MODE=lldb
shift
;;
-gud)
MODE=gud
shift
;;
...
# 约在200行左右,此处添加lldb的模式
case "$MODE" in
gdb)
init_gdb
$GDB -x $GDBSCR --args $LAUNCHER $JPARMS "$@" $JAVA_ARGS
rm -f $GDBSCR
;;
lldb)
lldb -- $LAUNCHER $JPARMS "$@" $JAVA_ARGS
;;
gud)
init_gdb
...
为了接下去的调试方便,先在当前的终端设置一下新的jdk目录变量,在build/macosx-x86_64-normal-server-fastdebug目录下执行下面的命令。
FASTDEBUG_HOME=`pwd`
NEW_JDK_HOME=`pwd`/jdk
创建一个工作目录,在工作目录下新建一个测试文件Hello.java。
mkdir $FASTDEBUG_HOME/workspace
cd $FASTDEBUG_HOME/workspace
Hello.java
public class Hello {
public static void main(String[] args) throws Exception {
System.out.println("Hello, world!");
}
}
编译Hello.java。
$NEW_JDK_HOME/bin/javac Hello.java
使用lldb调试Hello.class
$FASTDEBUG_HOME/hotspot/variant-server/libjvm/hotspot -lldb -cp . Hello
这样就进入了lldb的调试终端,使用b main打下我们的第一个断点。
/xxx/jvm/jdk9/build/macosx-x86_64-normal-server-fastdebug/hotspot/variant-server/libjvm
(lldb) target create "/xxx/jvm/jdk9/build/macosx-x86_64-normal-server-fastdebug/jdk/bin/java"
Current executable set to '/xxx/jvm/jdk9/build/macosx-x86_64-normal-server-fastdebug/jdk/bin/java' (x86_64).
(lldb) settings set -- target.run-args "-XXaltjvm=/xxx/jvm/jdk9/build/macosx-x86_64-normal-server-fastdebug/hotspot/variant-server/libjvm" "-Dsun.java.launcher.is_altjvm=true" "-cp" "." "Hello"
(lldb) b main
Breakpoint 1: 19 locations.
然后执行run就可以让jvm跑起来了,通过c指令来让进程继续,直到看到输出"Hello,world!"进程退出为止。
Target 0: (java) stopped.
(lldb) c
Process 3500 resuming
Hello, world!
Process 3500 exited with status = 0 (0x00000000)
至此,jdk9的编译和简单的调试算是完成了。