JAVA 11 MAC 源码安装与调试

2,803 阅读4分钟

参考原文

JAVA 11 是 JAVA 8 之后的第一个 LTS 版本,为了了解下一代 JAVA 版本更新的内容,也为了能调试到 JAVA 的内部代码,所以笔者决定源码安装试试。

安装

官方给出的安装教程[1]是这样的:

  1. 下载源码:hg clone http://hg.openjdk.java.net/jdk/jdk
  2. 执行configure:bash configure。缺失依赖会导致 configure 失败,但错误日志都会给出操作建议,根据建议安装依赖即可
  3. 执行 make:make images
  4. 验证新构建的 java 可用:./build/*/images/jdk/bin/java -version
  5. 测试:make run-test-tier1

这个安装步骤大体上没问题,但是有些差异需要指出,因此我也给出我的安装步骤。

安装依赖

安装 jre 11 运行时,这个可以通过 brew 来安装,或者直接下载 oracleopenjdk

因为编译的是比较新的 jdk 11,直接用 xcode 最新版即可(官方推荐的是 9.4,我用的是最新版 10.3 没问题)。

通过 brew 安装 autoconf 和 freetype。

获取源码

从 jdk 10 开始,源码不再分散在不同的仓库中,所以只需要 clone 单独的 repository 即可[1:1]。我选择是 jdk11u,而且不是通过 hg clone 的方式(比较慢,经常出错需要重试),而是直接下载整个源码包,如下图示。

jdk11 download
jdk11 download

编译

执行 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:2],执行 make 不带任何参数等同于make defaultmake 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:3]

  • 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 便可以正常查看代码了。

jdk11 classpath
jdk11 classpath

如果使用的是 $BUILD/images/jdk,直接将该目录加入到 classpath 即可,IDE 会自动识别 src.zip 并放在 sourcepath 中;

如果使用的是 $BUILD/jdk,由于这是 exploded image jdk,不包含源码,所以需要分别加入 classpath 和 sourcepath,sourcepath 即下载的 openjdk 下的 src 目录。

jdk11 sourcepath
jdk11 sourcepath

其他需要修改的配置不在赘述,如下图示。

jdk11 ide conf
jdk11 ide conf

配置调试

在 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。

clion debug jvm
clion debug jvm

导入之后,具体的 cpp 文件会报错,但是不影响调试,可以暂时忽略。

导入成功,reload CMakeLists.txt 之后,会自动生成一个 debug configuration。下面配置 debug configuration。如图所示,将 executable 改为你的 java 二进制文件,然后在 program arguments 里设置程序参数。我们先设置为 -version。

jdk11 debug1
jdk11 debug1

此时我们在share/prims/jni.cpp文件上打一个断点,然后执行 debug,就可以看到效果了。

jdk11 debug2
jdk11 debug2


  1. 每个下载的 jdk 中都有一个 build 文档,一般在 doc 目录下。这是编译安装该 jdk 的最权威的参考文档。jdk11 的在线 build 文档是:Building the JDK 11 ↩︎ ↩︎ ↩︎ ↩︎

  2. $BUILD 表示你构建出来的结果的目录,一般是你下载的 openjdk11 下的 build/xxx,xxx 是一个动态的名称,和你的开发机类型、编译参数有关,在我的机器上是 ./openjdk11/build/macosx-x86_64-normal-server-slowdebug。 ↩︎