好奇心害死羊
很多男伴都是做Java开发,每天写Java代码。 它们肯定离不开Java基础环境:JDK,虽然我们写的Java代码也是运行在JVM虚拟机上的。
一般来说,我们学习Java之前,第一步就是安装JDK环境。 这很简单。 我们通常直接从官网下载JDK。 安装完成后,我们通过环境变量就可以愉快的使用了。
不过话说回来,对于这个每天都在使用的东西,我们难道不好奇这个东西是如何从源码编译出来的吗?
带着这个原始的疑问,我打算明天干一番大事业,动动我可爱的小手,编译一个属于我自己的JDK!
还有一个坑需要填补
一个很实际的问题:
我们将JDK源代码解压到src.zip包中。 链接到这个源代码后,我们在调试的时候就可以进入,而我们添加注释的时候,只能添加在行尾,而不能改变原代码的行结构。 。 也就是说,如果在源代码中添加跨行的多行注释,调试时只会错位当前行源码重新编译,这就有点尴尬了。
有几个男同伴问过这个问题:
原因也很简单,因为真正支持调试运行的代码并不是我们解压出来的JDK源码,而是仅仅用于关联的JDK,而实际运行时使用的JDK是安装的JDK环境在之前的系统中。
要解决这个问题,只能用自己改过的代码编译生成自己的JDK,然后在项目中使用!
所以如果一切都忍住的话,肝脏就完蛋了!
环境规划的首选是编译前的软件版本之间的关系非常重要。 当我踩坑的时候,出现的各种奇怪的问题几乎都与此有关。 版本匹配之后,非常顺利。
我们盘点一下,整理一下编译一个JDK需要哪些环境和工具:
1.启动JDK
如果我们要编译JDK,首先我们自己的机器上必须提前安装好JDK,官方称之为bootstrapJDK(或者bootJDK)。
例如,如果要编译JDK8,则机器必须至少有一个JDK7或更高版本; 如果要编译JDK11,则机器上必须安装有JDK10或11。
那么先有鸡还是先有蛋的问题又来了…… 2. Unix 环境
编译JDK需要Unix环境的支持!
这在Linux操作系统和macOS操作系统上已经自然得到保证,但对于Windows兄弟来说就有点麻烦了,需要使用Cygwin或MinGW/MSYS等软件来模拟。
正如官方所说:在Linux平台上编译JDK通常问题最少,很容易成功; macOS 是下一个; 在Windows上,你需要多花一点功夫,而且可能会出现更多的问题。
本质原因是Windows不是类Unix内核系统。 虽然很多软件的原始编译都离不开UnixToolkit,但是肯定比较麻烦。
3. 编译器/编译工具链
JDK的底层源码很多(尤其是JVM虚拟机的部分)都是用C++/C编写的,所以相关的编译器跑不掉。
一张图片胜过千言万语。 各平台上的编译器支持情况如下表所示,您可以根据平台进行选择:
4.其他工具
典型例子:
好的,环境清单就这样了。 我分别列出我编译JDK8和JDK11时所使用的软件的详细版本信息:
编译JDK8时:
编译JDK11时:
如果编译过程中出现很多问题,很大概率是某些软件没有安装,或者软件版本不匹配,不要轻易放弃,需要耐心检查自己。
下载JDK源码
虽然有两种方法可以下载JDK源码。
方法一:通过Mercurial工具下载
Mercurial可以理解为像Git一样的另一种代码管理工具。 安装后,有一个 hg 命令可用。
OpenJDK的源码已经提前托管了。
为此,例如要下载JDK8源码重新编译,可以直接hgclone它,就像gitclone一样:
克隆
同样,下载JDK11:
克隆
然而,这些表格的下载速度并不是很快。
方法二:直接下载打包好的源码包
下载链接:
选择您要下载的版本。
编译前手动配置
下载完源码包后,放到本地目录(建议路径为纯韩文,以免造成不必要的麻烦),解压,然后进入源码根目录,执行:
配置文件
其实这里运行的是默认的配置项。
这一步会进行一系列的手动配置工作,而且时间通常很快。 如果最后能出现提示,那就很幸运了,编译前的配置工作就完成了!
这里我分别给出自己配置JDK11和JDK8时的样子:
配置JDK8完成:
配置JDK11完成:
注意:如果这一步出现错误,大概率是某个软件环境没有安装,或者虽然安装了,但是版本不匹配,通常会在控制台复制日志中提醒。
比如我配置JDK8的时候,遇到了errof: GCCcompilerisrequired的问题:
显然系统中已经有编译器了,但是还是报这个错误。通过后面更改jdk源码根目录/common/autoconf/ generated-configure.sh文件,注释掉相关两行代码后,配置为通过了
配置完成,是时候开始真正的编译动作了!
真正的编译动作
我们这里做的是全编译,直接在我们下载的JDK源码根目录下执行以下命令即可:
全部
这一步编译需要一点时间,耐心等待即可。 如果编译过程中出现错误,编译将会停止。 如果你能看到下面两个画面,那么恭喜你,你自己已经通过了JDK源码的编译,可以喝杯奶茶庆祝一下了。
JDK8编译完成:
JDK11编译完成:
从两张图的对比可以看出,JDK8和JDK11编译时输出还是有区别的。 时间上的差异很大程度上来自于JDK11的编译器配置要高得多。
验证结果
JDK源码编译完成后,肯定会形成并输出很多产品,这是我们迫不及待想要听到的。
由于JDK8和JDK11的源码包组织结构不同,所以输出的内容和位置也不同。 我们来一一盘点一下。
1.JDK8的编译输出
编译完成后,会在build目录下生成一个macosx-x86_64-normal-server-release目录,所有编译结果都位于其中。
首先,编译好的Java可执行程序可以在以下目录中找到:
jdk源码根目录 /build/macosx-x86_64-normal-server-release/jdk/bin
单步进入目录后,可以输入./java-version命令进行验证:
其次,在该目录下可以找到编译生成的成品JDK包
jdk源码根目录 /build/macosx-x86_64-normal-server-release/images
找到下面,如图:
在:
进入j2sdk-image目录,你会发现上面的内容和我们平时从网上下载的成品JDK内容是一致的。
2. JDK11的编译输出 JDK11的源代码目录组织与JDK8本身不同。 编译出来的产物与之前JDK8编译的输出有些不同,但也不是太大。
JDK11编译完成后,会在build目录下生成一个macosx-x86_64-normal-server-release目录,所有编译结果都位于其中。
目录中可以找到同样编译好的Java可执行程序
JDK源码根目录 /build/macosx-x86_64-normal-server-release/jdk/bin
如下所示,进入该目录后,还可以输入./java-version命令进行验证:
其次,在该目录下可以找到编译生成的JDK11汉化包
JDK源码根目录 /build/macosx-x86_64-normal-server-release/images
找到下面,如图:
jdk目录是编译生成的JDK11成品包。
使用自己编译的JDK
现在我们已经手工编译完成了JDK,接起来就得用了。
新建一个最基本的Java项目,例如命名为JdkTest,目的是使用我们自己编译的JDK。
我们点击ProjectStructure,选择SDKs选项,添加我们刚刚编译生成的JDK,并选择它作为项目的JDK,看看是否可以正常工作
点击确定后,我们运行一下:
可以看到我们自己编译的JDK已经被使用了。
关联JDK源码并更改
我们继续将JDK源码与上一步JdkTest项目的ProjectStructure→SDKs中自行下载的JDK源码路径关联起来:
这样方便我们在下载的JDK源码的源码中阅读、调试、修改以及做笔记和注释。
举个最简单的例子,我们打开函数System.out.println()的底层源码:
让我们稍微改变一下并添加两行简单的标记,如下所示:
为了让我们新添加的代码行生效,我们必须到JDK源码根目录下再次执行makeimages重新编译生成JDK才能生效:
由于之前已经完全编译过,所以再次make时增量编译通常会很快。
重新编译后,我们再次运行JdkTest项目来看看更改的效果:
多行注释的问题
你可能注意到一个问题:阅读源代码,对源代码做一些注释或注释是很常见的! 但当时存在一个问题,注释时不能改变代码的行结构(只能行尾注释,不能跨行注释),否则调试时会出现行号错位的问题。
原因很简单,因为我们好像映射了源码目录,而实际支持运行的JDK仍然是预装的JDK环境,并没有根据我们改变的源码重新编译建立,所以看这里,解决这个问题很简单,像以前一样自己编译JDK即可。
其实在实验过程中,还有一个非常典型的问题。 添加多行英文注释后,再次编译就会报错!
例如,仍然以上例中最简单的System.out.println()源码为例,我们添加几行英文注释:
这时候,当我们进入JDK源码目录进行编译时,会发现满屏都是这样的错误:
错误:无法映射字符用于编码 ascii
突然我有点困惑了,虽然我只加了几行注释。 对于我们来说,在源码中写一些多行英文注释基本上是必须的,但是编译的时候就会报错。 这能让人玩得开心吗……当时我的背有点凉。
说实话,我研究这个问题有一段时间了,熬夜很晚。 最后经过一番折腾,我通过以下方法解决了。 顺便分享给我的男同伴们。 如果您遇到这个问题,可以参考一下来解决。
从控制台报错可以明显看出,肯定是字符编码相关的问题引起的,而且都指向ascii的编码形式。
于是我把根目录下的JDK的源码导出到VsCode中,然后在整个目录下搜索编码ascii相关的内容,看看有没有疲劳感,结果发现
jdk源码根目录/make/common/SetupJavaCompilers.gmk文件中有两个地方指定了ascii相关的编码方式:
因此,尝试将 -encodingascii 替换为 -encodingutf-8:
之后,再次执行makeimages编译,编译顺利通过!
你完成了!
这样,阅读、调试或者定制JDK源码就非常方便了。
结尾
如何评估一个开源项目的优劣?