羊老姆上线:抄起键盘就编译JDK源码,结果上头了

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

好奇害死羊

很多小伙伴们做 Java
开发,天天写 Java
代码,肯定离不开 Java
基础环境: JDK
,毕竟我们写好的 Java
代码也是跑在 JVM
虚拟机上。

一般来说,我们学 Java
之前,第一步就是安装 JDK
环境。这个简单啊,我们一般直接把 JDK
从官网下载下来,安装完成,配个环境变量就可以愉快地使用了。

不过话说回来,对于这个天天使用的东西,我们难道不好奇这玩意儿它到底是怎么由源码编译出来的吗?

带着这个原始的疑问,今天准备大干一场,自己动动呆萌的小手,来编译一个属于自己的 JDK
吧!

还有个待填的坑

记得之前不是出过一期关于 《JDK源码阅读环境搭建》
相关的视频以及文章嘛,细心的小伙伴,可能会发现一个 很实际的问题

我们将 src.zip
包里的 JDK
源码解压出来,关联到这份源码之后,调试时是可以进,但是我们在加注释的时候却只能在 行尾添加
,并不能改变原代码的行结构。换句话说,如果在源码中加了跨行的多行注释,则 debug
调试的时候就会出现当前行的 运行错位
问题,这个有点尴尬了。

当然那个视频的评论区,的确也有几个小伙伴提了这个问题:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了
羊老姆上线:抄起键盘就编译JDK源码,结果上头了
羊老姆上线:抄起键盘就编译JDK源码,结果上头了

原因也很简单,因为实际支撑调试运行的代码,并不是我们解压出来的那份 JDK
源码,那个仅仅是做关联用,实际运行用到的 JDK
,还是之前系统安装好的那个 JDK
环境。

要想解决这个问题,那就只能使用自己修改过的代码来 自行编译生成
自己的 JDK
,然后用到项目中去!

所以什么都憋说了,肝就完了!

环境准备

首选说在前面的是,编译前的软件版本关系极其重要,自己在踩坑时,所出现的各种奇奇怪怪的问题几乎都和这个有关,后来版本匹配之后,就非常顺利了。

我们来 盘点和梳理
一下编译一个JDK需要哪些环境和工具:


1、boot JDK

我们要想编译 JDK
,首先自己本机必须提前已经安装有一个 JDK
,官方称之为 bootstrap JDK
(或者称为 boot JDK
)。

比如想编译 JDK 8
,那本机必须最起码得有一个 JDK 7
或者更新一点的版本;你想编译 JDK 11
,那就要求本机必须装有 JDK 10
或者 11

所以鸡生蛋、蛋生鸡又来了…


2、Unix环境

编译 JDK
需要 Unix
环境的支持!

这一点在 Linux
操作系统macOS
操作系统上已经天然的保证了,而对于 Windows
兄弟来说稍微麻烦一点,需要通过使用 Cygwin
或者 MinGW/MSYS
这种软件来模拟。

就像官方所说:在 Linux
平台编译 JDK
一般问题最少,容易成功; macOS
次之; Windows
上则需要稍微多花点精力,问题可能也多一些。

究其本质原因,还是因为 Windows
毕竟不是一个 Unix-Like
内核的系统,毕竟很多软件的原始编译都离不开 Unix Toolkit
,所以相对肯定要麻烦一些。


3、编译器/编译工具链

JDK
底层源码(尤其 JVM
虚拟机部分)很多都是 C++/C
写的,所以相关编译器也跑不掉。

一图胜千言,各平台上的编译器支持如下表所示,按平台选择即可:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了


4、其他工具

典型的比如:

  • Autoconf
    :软件源码包的自动配置工具
  • Make
    :编译构建工具
  • freetype
    :一个免费的渲染库, JDK
    图形化部分的代码可能会用它

好,环境盘点就到这里,接下来具体列一下我在编译 JDK 8
JDK 11
时分别用到的软件详细版本信息:

编译JDK 8时:

  • 操作系统
    :macOS 10.11.6
  • boot JDK
    :JDK 1.8.0 (build 1.8.0_201-b09)
  • Xcode版本
    :8.2
  • 编译器
    :Version 8.0.0 (at /usr/bin/clang)

编译JDK 11时:

  • 操作系统
    :macOS 10.15.4
  • boot JDK
    :JDK 11.0.7 (build 11.0.7+8-LTS)
  • Xcode版本
    :11.5
  • 编译器
    :Version 11.0.3 (at /usr/bin/clang)

大家在编译时如果过程中有很多问题,大概率少软件没装,或者软件版本不匹配,不要轻易放弃,需要耐心自查一下。

下载JDK源码

下载 JDK
源码其实有两种方式。


方式一:通过Mercurial工具下载

Mercurial
可以理解为和 Git
一样,是另外一种代码管理工具,安装好之后就有一个 hg
命令可用。

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

OpenJDK
的源码已经提前托管到 http://hg.openjdk.java.net/

因此,比如下载 JDK 8
,可直接 hg clone
一下就行,和 git clone
一样:

hg clone http://hg.openjdk.java.net/jdk8/jdk8

同理,下载 JDK 11

hg clone http://hg.openjdk.java.net/jdk/jdk11

但是这种方式下载速度不是很快。


方式二:直接下载打包好的源码包

下载地址: https://jdk.java.net/

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

选择你想要的版本下载即可。

编译前的自动配置

源码包下载好,放到本地某个目录(建议路径纯英文,避免不必要的麻烦),解压之,然后进入源码根目录,执行:

sh configure

当然这里运行的是默认配置项。

这一步会进行一系列的自动配置工作,时间一般很快,最终如果能出现一下提示,那么很幸运,编译前的配置工作就完成了!

这里我给出我自己分别在配置 JDK 11
JDK 8
时候完成时的样子:

配置JDK 8完成:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

配置JDK 11完成:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

注:如果这一步出错,大概率是某个软件环境未装,或者即使装了,但版本不匹配,控制台打印日志里一般是会提醒的。

比如我在配置 JDK 8
的时候,就遇到了一个 errof:GCC compiler is required
的问题:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

明明系统里已经有编译器,但还是报这个错误。通过后来修改 jdk源码根目录/common/autoconf/generated-configure.sh
文件,将相关的两行代码注释后就配置通过了

羊老姆上线:抄起键盘就编译JDK源码,结果上头了
羊老姆上线:抄起键盘就编译JDK源码,结果上头了

配置完成,接下来开始执行真正的编译动作了!

真正的编译动作

我们这里进行的是全量编译,直接在我们下载的 JDK
源码根目录下执行如下命令即可:

make all

这一步编译需要一点时间,耐心等待一下即可。编译过程如果有错误,会终止编译,如果能看到如下两个画面,那么则恭喜你,自己编译 JDK
源码就已经通过了,可以搞一杯咖啡庆祝一下了。

JDK 8编译完成:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

JDK 11编译完成:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

从两张图的对比可以看出,编译 JDK 8
JDK 11
完成时在输出上还是有区别的。时间上的区别很大程度上来源于 JDK 11
的编译机配置要高不少。

验证成果

JDK
源码编译完成之后肯定会产生和输出很多产物,这也是我们所迫不及待想看到的。

由于 JDK 8
JDK 11
的源码包组织结构并不一样,所以输出东西的内容和位置也有区别。我们一一来盘点一下。


1、JDK 8的编译输出

编译完成, build
目录下会生成一个 macosx-x86_64-normal-server-release
目录,所有的编译成果均位于其中。

首先,编译出来的 Java
可执行程序可以在如下目录里找到:

jdk源码根目录/build/macosx-x86_64-normal-server-release/jdk/bin

进入该目录后,可以输入 ./java -version
命令验证:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

其次,编译生成的成品 JDK
套装,可以在目录

jdk源码根目录/build/macosx-x86_64-normal-server-release/images

下找到,如图所示:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

其中:

  • j2sdk-image
    :编译生成的JDK
  • j2re-image
    :编译生成的JRE

进入 j2sdk-image
目录会发现,里面的内容和我们平时从网络上下载的成品 JDK
内容一致。

羊老姆上线:抄起键盘就编译JDK源码,结果上头了


2、JDK 11的编译输出

JDK 11的源码目录组织方式和JDK 8本身就有区别,编译生成的产物和上面编译JDK 8的输出有一定区别,但也不大。

JDK 11
编译完成,同样在 build
目录下会生成一个 macosx-x86_64-normal-server-release
目录,所有的编译成果均位于其中。

同样编译出来的Java可执行程序可以在目录

JDK源码根目录/build/macosx-x86_64-normal-server-release/jdk/bin

下看到,进入该目录后,也可以输入 ./java -version
命令验证:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

其次,编译生成的成品 JDK 11
套装,可以在目录

JDK源码根目录/build/macosx-x86_64-normal-server-release/images

下找到,如图所示:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

其中 jdk
目录就是编译生成的成品 JDK 11
套装。

使用自己编译的JDK

既然我们已经动手编译出了 JDK
成品,接下来我们得用上哇。

新建一个最最基本的 Java
工程,比如命名为 JdkTest
,目的是把我们自己编译出的 JDK
给用上。

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

我们点开 Project Structure
,选到 SDKs
选项,新添加上自己刚刚编译生成的JDK,并选为项目的JDK,看看是否能正常工作

羊老姆上线:抄起键盘就编译JDK源码,结果上头了
羊老姆上线:抄起键盘就编译JDK源码,结果上头了

点击确定之后,我们运行之:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

可以看到我们自己编译出的JDK已经用上了。

关联JDK源码并修改

我们继续在上一步 JdkTest
项目的 Project Structure
→  SDKs
里将 JDK
源码关联到自行下载的JDK源码路径上:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

这样方便我们对自己下载的 JDK源码
进行 阅读
调试
修改
、以及在源码里随意 做笔记
加注释

举个最简单的例子,比如我们打开 System.out.println()
这个函数的底层源码:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

我们随便给它修改一下,加两行简单的标记,像这样:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

为了使我们新加的代码行生效,我们必须要重新去JDK源码的根目录中再次执行 make images
重新编译生成JDK方可生效:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

因为之前已经全量编译过了,所以再次 make
的时候增量编译一般很快。

重新编译之后,我们再次运行 JdkTest
项目,就可以看到改动的效果了:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

多行注释的问题

记得之前搭建《JDK源码阅读环境》时,大家可能发现了一个问题:阅读源码嘛,给源代码做点注释或笔记很常见!但那时候有个问题就是做注释时 不可改变代码的行结构
(只能行尾注释,不能跨行注释),否则debug调试时会出现 行号错位
的问题。

原因很简单,因为我们虽然做了源代码目录的映射,但是实际支撑运行的 JDK
还是预先安装好的那个JDK环境,并不是根据我们修改后的源码来重新编译构建的,所以看到这里,解决这个问题就很简单,就像上面一样自行编译一下 JDK
即可。

实际在实验时,还有一个很典型的问题是,当添加了多行的中文注释后,再编译居然会报错!

比如,还是以上面例子中最简单的 System.out.println()
源码为例,我们添加几行中文注释:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

这时候我们去JDK源码目录下编译会发现满屏类似这样的报错:

错误: 编码 ascii 的不可映射字符

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

顿时有点懵,毕竟仅仅是加了几行注释。对于我们来说,源码里写点多行的中文注释基本是 刚需
,然而编译竟会报错,这还能不能让人愉快的玩耍了… 当时后背有点发凉。

实不相瞒,就这个问题排查了一段时间,熬到了很晚。最终折腾了一番,通过如下这种方式解决了,顺便分享给小伙伴们,大家如果遇到了这个问题,可以参考着解决一下。

因为从控制台的报错可以很明显的看出,肯定是字符编码相关的问题导致的,而且都指向了 ascii
这种编码方式。

于是将JDK的源码从根目录导入了Vs Code,然后全目录查找 encoding ascii
相关的内容,看看有没有什么端倪,结果发现

jdk源码根目录/make/common/SetupJavaCompilers.gmk
文件中有两处指定了 ascii
相关的编码方式:

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

于是尝试将这两处 -encoding ascii
的均替换成 -encoding utf-8

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

然后再次执行 make images
编译,编译顺利通过!

羊老姆上线:抄起键盘就编译JDK源码,结果上头了

至此大功告成!

这样后面不管是 阅读
调试
还是 定制
JDK
源码都非常方便了。

这次分享就到这里吧,小伙伴们下篇见!

每天进步一点点

慢一点才能更快


给个[ 在看
],是对程序羊最大的支持

原文 

http://mp.weixin.qq.com/s?__biz=MzU4ODI1MjA3NQ==&mid=2247488474&idx=1&sn=f0fe2fbf6a60cd2d3cbb3022d29f4a9d

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 羊老姆上线:抄起键盘就编译JDK源码,结果上头了

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址