JAVA 11 是 JAVA 8 之后的第一个 LTS 版本,为了了解下一代 JAVA 版本更新的内容,也为了能调试到 JAVA 的内部代码,所以笔者决定源码安装试试。
安装
官方给出的安装教程1是这样的:
- 下载源码:hg clone http://hg.openjdk.java.net/jdk/jdk
- 执行configure:bash configure。缺失依赖会导致 configure 失败,但错误日志都会给出操作建议,根据建议安装依赖即可
- 执行 make:make images
- 验证新构建的 java 可用:./build/*/images/jdk/bin/java -version
- 测试:make run-test-tier1
这个安装步骤大体上没问题,但是有些差异需要指出,因此我也给出我的安装步骤。
安装依赖
安装 jre 11 运行时,这个可以通过 brew 来安装,或者直接下载 oracle 或 openjdk。
因为编译的是比较新的 jdk 11,直接用 xcode 最新版即可(官方推荐的是 9.4,我用的是最新版 10.3 没问题)。
通过 brew 安装 autoconf 和 freetype。
获取源码
从 jdk 10 开始,源码不再分散在不同的仓库中,所以只需要 clone 单独的 repository 即可1。我选择是 jdk11u,而且不是通过 hg clone 的方式(比较慢,经常出错需要重试),而是直接下载整个源码包,如下图示。
编译
执行 configure,为了 debug,需要加上对应的参数。
sh configure --with-target-bits=64 --enable-ccache --with-jvm-variants=server --with-boot-jdk-jvmargs="-Xlint:deprecation -Xlint:unchecked" --disable-warnings-as-errors --with-debug-level=slowdebug 2>&1 | tee configure_mac_x64.log
一般第一次执行总是会遇到些小问题,但编译 jdk 11 的问题比 jdk8,9少多了,根据提示很容易就可以解决。当看到如下返回即表明配置成功。
====================================================
A new configuration has been successfully created in
/Users/haidao/Downloads/openjdk11/build/macosx-x86_64-normal-server-slowdebug
using configure arguments '--with-target-bits=64 --enable-ccache --with-jvm-variants=server --with-boot-jdk-jvmargs='-Xlint:deprecation -Xlint:unchecked' --disable-warnings-as-errors --with-debug-level=slowdebug'.
Configuration summary:
* Debug level: slowdebug
* HS debug level: debug
* JVM variants: server
* JVM features: server: 'aot cds cmsgc compiler1 compiler2 dtrace epsilongc g1gc graal jfr jni-check jvmci jvmti management nmt parallelgc serialgc services vm-structs'
* OpenJDK target: OS: macosx, CPU architecture: x86, address length: 64
* Version string: 11-internal+0-adhoc.haidao.openjdk11 (11-internal)
Tools summary:
* Boot JDK: openjdk version "11.0.2" 2019-01-15 OpenJDK Runtime Environment 18.9 (build 11.0.2+9) OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode) (at /Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home)
* Toolchain: clang (clang/LLVM from Xcode 10.3)
* C Compiler: Version 10.0.1 (at /usr/bin/clang)
* C++ Compiler: Version 10.0.1 (at /usr/bin/clang++)
Build performance summary:
* Cores to use: 4
* Memory limit: 8192 MB
* ccache status: Active (3.7.2)
然后执行 make 即可。根据 build 文档1,执行 make 不带任何参数等同于make default和make jdk,这会 build 出一个较小的编译结果,并提供一个 exploded image。不知道怎么翻译 exploded image,大概意思是,这是一个分解开的镜像,可以直接使用,各个模块都是解压好的,不包含源码。这种设计是为了方便 jdk 的开发者渐进式开发,每次 make 只会 recompile 变化的部分。
其他 make 的常用 target 如下:
- hotspot - Build all of hotspot (but only hotspot)
- hotspot-<variant> - Build just the specified jvm variant
- images or product-images - Build the JDK image
- docs or docs-image - Build the documentation image
- test-image - Build the test image
- all or all-images - Build all images (product, docs and test)
- bootcycle-images - Build images twice, second time with newly built JDK (good for testing)
- clean - Remove all files generated by make, but not those generated by configure
- dist-clean - Remove all files, including configuration
我们以 $BUILD 表示构建结果目录2,构建结果目录如下1:
- jdk: 这就是之前所说的 exploded image 的目录。执行make jdk之后,你可以通过运行$BUILD/jdk/bin/java直接启动新构建的 JDK。
- images: 这个目录是 make *-image 的输出位置。例如,make jdk-image 会构建出 jdk image, 目录是images/jdk。
- test-results: 测试结果目录。
- support: 这个目录保存的是 build 过程中的中间文件,比如源码,对象文件,类文件等。support中比较重要的是gensrc,它包含生成的源码;modules_* 包含了按模块层级分布的文件,它会在之后合并到 jdk 目录下。
由于我们执行的是最简构建,我们主要看下 $BUILD/jdk 和 $BUILD/images/jdk 的差异:
$BUILD/images/jdk $BUILD/jdk
├── bin ├── _packages_attribute.done
├── conf ├── bin
├── demo ├── conf
├── include ├── include
├── jmods ├── lib
├── legal ├── modules
├── lib └── release
├── man
└── release
和 $BUILD/jdk 不一样,$BUILD/images/jdk 这个目录下没有解压好的 modules 目录,而是以压缩包的形式放在 jmods 下。这是自 JAVA 9 引入的模块化打包设计,旨在减小 JAVA 应用的包体积,使得部署更加轻量。
$BUILD/jdk 下的 lib 目录是动态库文件和调试信息文件,不包含源码。而 $BUILD/images/jdk 下的 lib 目录下的动态库文件没有可执行权限,但包含源码 src.zip。
$BUILD/jdk 作为 exploded image,不像 product-image 那样需要包含法律文件和 demo。
除了这些区别之外,二者在使用上没啥差别。
现在,可以直接通过 $BUILD/jdk/bin/java 来使用编译出来的 JAVA11。你也可以配置 jenv:jenv add $BUILD/jdk。关于 jenv 的使用请参看 Mac OS 使用 jenv 管理 java 版本。
配置 IDEA
增加 SDK,配置 classpath 和 sourcepath。配置好 sourcepath 便可以正常查看代码了。
如果使用的是 $BUILD/images/jdk,直接将该目录加入到 classpath 即可,IDE 会自动识别 src.zip 并放在 sourcepath 中;
如果使用的是 $BUILD/jdk,由于这是 exploded image jdk,不包含源码,所以需要分别加入 classpath 和 sourcepath,sourcepath 即下载的 openjdk 下的 src 目录。
其他需要修改的配置不在赘述,如下图示。
配置调试
在 IDE sdk 中配置好 jdk 11 之后便可以正常调试 JAVA 代码了。如果我们想调试 jdk/jvm 源码的 C 代码怎么办呢?我们已经 build 一个可以 debug 的 java11 运行环境,下面就是配置 IDE 来 debug。
clion 调试 jvm 代码
首先导入源码。使用 New Cmake Project from Sources,这样可以自动创建 CMakeLists.txt 文件。然后按照引导即可导入源码。你可以导入 src/hotspot,也可以整个导入 src。
导入之后,具体的 cpp 文件会报错,但是不影响调试,可以暂时忽略。
导入成功,reload CMakeLists.txt 之后,会自动生成一个 debug configuration。下面配置 debug configuration。如图所示,将 executable 改为你的 java 二进制文件,然后在 program arguments 里设置程序参数。我们先设置为 -version。
此时我们在share/prims/jni.cpp文件上打一个断点,然后执行 debug,就可以看到效果了。