1.Java技术体系
把Java程序设计语言、 Java虚拟机、 Java类库这三部分统称为JDK(Java Development Kit) , JDK是用于支持Java程序开发的最小环境。
把Java类库API中的Java SE API子集[3]和Java虚拟机这两部分统称为JRE(Java Runtime Environment) , JRE是支持Java程序运行的标准环境。
图1-1 Java技术体系所包括的内容
2 Java发展史
图1-2 Java技术发展的时间线
3.自己编译OpenJDK
图1-3 OpenJDK和OracleJDK之间的关系
OpenJDK和OracleJDK在程序上是非常接近的, 两者共用了绝大部分相同的代码(如图1-7所示, 注意图中的英文提示了两者共同代码的占比要远高于图形上看到的比例) , 所以我们编译的OpenJDK, 基本上可以认为性能、 功能和执行逻辑上都和官方的OracleJDK是一致的。
图1-4 OpenJDK版本之间的关系
1).获取OpenJDK源码,Repository地址https://hg.openjdk.java.net/jdk/jdk12。要求编译OpenJDK至少需要2~4GB的内存空间(CPU核心数越多, 需要的内存越大) , 而且至少要6~8GB的空闲磁盘空间, 不要看OpenJDK源码的大小只有不到600MB, 要完成编译, 过程中会产生大量的中间文件, 并且编译出不同优化级别(Product、 FastDebug、 SlowDebug) 的HotSpot虚拟机可能要重复生成这些中间文件, 这都会占用大量磁盘空间。所有的文件, 包括源码和依赖项目, 都不要放在包含中文的目录里面, 这样做不是一定会产生不可解决的问题, 只是没有必要给自己找麻烦。
2).对于Linux系统,必须确保最低的版本为GCC 4.8或者CLang 3.2以上,官方推荐使用GCC7.8或者CLang9.1来完成编译。在Ubuntu系统上安装GCC的命令为:
sudo apt-get install build-essential
在编译过程中需要依赖FreeType、 CUPS等若干第三方库, OpenJDK全部的依赖库已在表1-1中列出, 读者可执行相应的安装命令完成安装。
表1-1 OpenJDK编译依赖库
最后, 假设要编译大版本号为N的JDK, 我们还要另外准备一个大版本号至少为N-1的、 已经编译好的JDK, 这是因为OpenJDK由多个部分(HotSpot、 JDK类库、 JAXWS、 JAXP……) 构成, 其中一部分(HotSpot) 代码使用C、 C++编写, 而更多的代码则是使用Java语言来实现, 因此编译这些Java代码就需要用到另一个编译期可用的JDK, 官方称这个JDK为“Bootstrap JDK”。 编译OpenJDK 12时,Bootstrap JDK必须使用JDK 11及之后的版本。 在Ubuntu中使用以下命令安装OpenJDK 11:
sudo apt-get install openjdk-11-jdk
3).进行编译。了解OpenJDK提供的编译参数可以使用“bash configure--help”命令。
configure命令承担了依赖项检查、 参数配置和构建输出目录结构等多项职责, 如果编译过程中需要的工具链或者依赖项有缺失, 命令执行后将会得到明确的提示, 并且给出该依赖的安装命令。
例如,编译FastDebug版、 仅含Server模式的HotSpot虚拟机, 命令应为:
bash configure --enable-debug --with-jvm-variants=server
在configure命令以及后面的make命令的执行过程中, 会在“build/配置名称”目录下产生如下目录结构。如果多次编译, 或者目录结构成功产生后又再次修改了配置, 必须先使用“make clean”和“make dist-clean”命令清理目录, 才能确保新的配置生效。 编译产生的目录结构以及用途如下所示:
buildtools/: 用于生成、 存放编译过程中用到的工具 hotspot/: HotSpot虚拟机编译的中间文件 images/: 使用make *-image产生的镜像存放在这里 jdk/: 编译后产生的JDK就放在这里 support/: 存放编译时产生的中间文件 test-results/: 存放编译后的自动化测试结果 configure-support/: 这三个目录是存放执行configure、 make和test的临时文件 make-support/ test-support/
依赖检查通过后便可以输入“make images”执行整个OpenJDK编译了, 这里“images”是“productimages”编译目标(Target) 的简写别名,这个目标的作用是编译出整个JDK镜像, 除了“productimages”以外, 其他编译目标还有:
hotspot: 只编译HotSpot虚拟机 hotspot-<variant>: 只编译特定模式的HotSpot虚拟机 docs-image: 产生JDK的文档镜像 test-image: 产生JDK的测试镜像 all-images: 相当于连续调用product、 docs、 test三个编译目标 bootcycle-images: 编译两次JDK, 其中第二次使用第一次的编译结果作为Bootstrap JDK clean: 清理make命令产生的临时文件 dist-clean: 清理make和configure命令产生的临时文件
编译完成之后, 进入OpenJDK源码的“build/配置名称/jdk”目录下就可以看到OpenJDK的完整编译结果了, 把它复制到JAVA_HOME目录, 就可以作为一个完整的JDK来使用, 如果没有人为设置过JDK开发版本的话, 这个JDK的开发版本号里默认会带上编译的机器名, 如下所示:
> ./java -version openjdk version "12-internal" 2019-03-19 OpenJDK Runtime Environment (build 12-internal+0-adhoc.icyfenix.jdk12) OpenJDK 64-Bit Server VM (build 12-internal+0-adhoc.icyfenix.jdk12, mixed mode)
4).在IDE工具中进行源码调试。
采用的IDE是JetBrains的CLion。
HotSpot增加了参数来方便开发人员调试解释器:-XX:+TraceBytecodes -XX:StopInterpreterAt=<n>。
这组参数的作用是当遇到序号为<n>的字节码指令时, 便会中断程序执行, 进入断点调试。 调试解释器部分代码时, 把这两个参数加到java命令的参数后面即可。完成以上配置之后, 一个可修改、 编译、 调试的HotSpot工程就完全建立起来了, Hot-Spot虚拟机启动器的执行入口是java.c的JavaMain()方法。
图1-5 在CLion中创建HotSpot项目