不少小伙伴们作Java
开发,每天写Java
代码,确定离不开Java
基础环境:JDK
,毕竟咱们写好的Java
代码也是跑在JVM
虚拟机上。java
通常来讲,咱们学Java
以前,第一步就是安装JDK
环境。这个简单啊,咱们通常直接把JDK
从官网下载下来,安装完成,配个环境变量就能够愉快地使用了。git
不过话说回来,对于这个每天使用的东西,咱们难道很差奇这玩意儿它究竟是怎么由源码编译出来的吗?github
带着这个原始的疑问,今天准备大干一场,本身动动呆萌的小手,来编译一个属于本身的JDK
吧!面试
对了,本文在开源项目: https://github.com/hansonwang99/JavaCollection 中已收录,包含自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中...
记得以前不是出过一期关于《JDK源码阅读环境搭建》相关的视频以及文章嘛,细心的小伙伴,可能会发现一个很实际的问题:macos
咱们将src.zip
包里的JDK
源码解压出来,关联到这份源码以后,调试时是能够进,可是咱们在加注释的时候却只能在 行尾添加,并不能改变原代码的行结构。换句话说,若是在源码中加了跨行的多行注释,则debug
调试的时候就会出现当前行的 运行错位问题,这个有点尴尬了。
固然那个视频的评论区,的确也有几个小伙伴提了这个问题:编程
缘由也很简单,由于实际支撑调试运行的代码,并非咱们解压出来的那份JDK
源码,那个仅仅是作关联用,实际运行用到的JDK
,仍是以前系统安装好的那个JDK
环境。bootstrap
要想解决这个问题,那就只能使用本身修改过的代码来自行编译生成本身的JDK
,而后用到项目中去!网络
因此什么都憋说了,肝就完了!函数
首选说在前面的是,编译前的软件版本关系极其重要,本身在踩坑时,所出现的各类奇奇怪怪的问题几乎都和这个有关,后来版本匹配以后,就很是顺利了。
咱们来盘点和梳理一下编译一个JDK须要哪些环境和工具:工具
咱们要想编译JDK
,首先本身本机必须提早已经安装有一个JDK
,官方称之为bootstrap JDK
(或者称为boot JDK
)。
好比想编译JDK 8
,那本机必须最起码得有一个JDK 7
或者更新一点的版本;你想编译JDK 11
,那就要求本机必须装有JDK 10
或者11
。
因此鸡生蛋、蛋生鸡又来了...
编译JDK
须要Unix
环境的支持!
这一点在Linux
操做系统和macOS
操做系统上已经自然的保证了,而对于Windows
兄弟来讲稍微麻烦一点,须要经过使用Cygwin
或者MinGW/MSYS
这种软件来模拟。
就像官方所说:在Linux
平台编译JDK
通常问题最少,容易成功;macOS
次之;Windows
上则须要稍微多花点精力,问题可能也多一些。
究其本质缘由,仍是由于Windows
毕竟不是一个Unix-Like
内核的系统,毕竟不少软件的原始编译都离不开Unix Toolkit
,因此相对确定要麻烦一些。
JDK
底层源码(尤为JVM
虚拟机部分)不少都是C++/C
写的,因此相关编译器也跑不掉。
一图胜千言,各平台上的编译器支持以下表所示,按平台选择便可:
典型的好比:
Autoconf
:软件源码包的自动配置工具Make
:编译构建工具freetype
:一个免费的渲染库,JDK
图形化部分的代码可能会用它好,环境盘点就到这里,接下来具体列一下我在编译JDK 8
和JDK 11
时分别用到的软件详细版本信息:
编译JDK 8时:
操做系统
:macOS 10.11.6boot 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.4boot JDK
:JDK 11.0.7 (build 11.0.7+8-LTS)Xcode版本
:11.5编译器
:Version 11.0.3 (at /usr/bin/clang)你们在编译时若是过程当中有不少问题,大几率少软件没装,或者软件版本不匹配,不要轻易放弃,须要耐心自查一下。
下载JDK
源码其实有两种方式。
Mercurial
能够理解为和Git
同样,是另一种代码管理工具,安装好以后就有一个hg
命令可用。
而OpenJDK
的源码已经提早托管到http://hg.openjdk.java.net/
。
所以,好比下载JDK 8
,可直接hg clone
一下就行,和git clone
同样:
`hg clone http://hg.openjdk.java.net/jd...
`
同理,下载JDK 11
:
`hg clone http://hg.openjdk.java.net/jd...
`
可是这种方式下载速度不是很快。
下载地址:https://jdk.java.net/
选择你想要的版本下载便可。
源码包下载好,放到本地某个目录(建议路径纯英文,避免没必要要的麻烦),解压之,而后进入源码根目录,执行:
`sh configure
`
固然这里运行的是默认配置项。
这一步会进行一系列的自动配置工做,时间通常很快,最终若是能出现一下提示,那么很幸运,编译前的配置工做就完成了!
这里我给出我本身分别在配置JDK 11
和JDK 8
时候完成时的样子:
配置JDK 8完成:
配置JDK 11完成:
注: 若是这一步出错,大几率是某个软件环境未装,或者即便装了,但版本不匹配,控制台打印日志里通常是会提醒的。
好比我在配置JDK 8
的时候,就遇到了一个errof:GCC compiler is required
的问题:
明明系统里已经有编译器,但仍是报这个错误。经过后来修改 jdk源码根目录/common/autoconf/generated-configure.sh
文件,将相关的两行代码注释后就配置经过了
配置完成,接下来开始执行真正的编译动做了!
咱们这里进行的是全量编译,直接在咱们下载的JDK
源码根目录下执行以下命令便可:
`make all
`
这一步编译须要一点时间,耐心等待一下便可。编译过程若是有错误,会终止编译,若是能看到以下两个画面,那么则恭喜你,本身编译JDK
源码就已经经过了,能够搞一杯咖啡庆祝一下了。
JDK 8编译完成:
JDK 11编译完成:
从两张图的对比能够看出,编译JDK 8
和JDK 11
完成时在输出上仍是有区别的。时间上的区别很大程度上来源于JDK 11
的编译机配置要高很多。
JDK
源码编译完成以后确定会产生和输出不少产物,这也是咱们所火烧眉毛想看到的。
因为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源码根目录/build/macosx-x86_64-normal-server-release/images
`
下找到,如图所示:
其中:
j2sdk-image
:编译生成的JDKj2re-image
:编译生成的JRE进入j2sdk-image
目录会发现,里面的内容和咱们平时从网络上下载的成品JDK
内容一致。
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 11
套装,能够在目录
`JDK源码根目录/build/macosx-x86_64-normal-server-release/images
`
下找到,如图所示:
其中jdk
目录就是编译生成的成品JDK 11
套装。
既然咱们已经动手编译出了JDK
成品,接下来咱们得用上哇。
新建一个最最基本的Java
工程,好比命名为JdkTest
,目的是把咱们本身编译出的JDK
给用上。
咱们点开Project Structure
,选到SDKs
选项,新添加上本身刚刚编译生成的JDK,并选为项目的JDK,看看是否能正常工做
点击肯定以后,咱们运行之:
能够看到咱们本身编译出的JDK已经用上了。
咱们继续在上一步JdkTest
项目的Project Structure
→ SDKs
里将JDK
源码关联到自行下载的JDK源码路径上:
这样方便咱们对本身下载的JDK源码
进行阅读、调试、修改、以及在源码里随意作笔记和加注释。
举个最简单的例子,好比咱们打开System.out.println()
这个函数的底层源码:
咱们随便给它修改一下,加两行简单的标记,像这样:
为了使咱们新加的代码行生效,咱们必需要从新去JDK源码的根目录中再次执行 make images
从新编译生成JDK方可生效:
由于以前已经全量编译过了,因此再次make
的时候增量编译通常很快。
从新编译以后,咱们再次运行JdkTest
项目,就能够看到改动的效果了:
记得以前搭建《JDK源码阅读环境》时,你们可能发现了一个问题:阅读源码嘛,给源代码作点注释或笔记很常见!但那时候有个问题就是作注释时不可改变代码的行结构(只能行尾注释,不能跨行注释),不然debug调试时会出现行号错位的问题。
缘由很简单,由于咱们虽然作了源代码目录的映射,可是实际支撑运行的JDK
仍是预先安装好的那个JDK环境,并非根据咱们修改后的源码来从新编译构建的,因此看到这里,解决这个问题就很简单,就像上面同样自行编译一下JDK
便可。
实际在实验时,还有一个很典型的问题是,当添加了多行的中文注释后,再编译竟然会报错!
好比,仍是以上面例子中最简单的System.out.println()
源码为例,咱们添加几行中文注释:
这时候咱们去JDK源码目录下编译会发现满屏相似这样的报错:
错误: 编码 ascii 的不可映射字符
顿时有点懵,毕竟仅仅是加了几行注释。对于咱们来讲,源码里写点多行的中文注释基本是刚需,然而编译竟会报错,这还能不能让人愉快的玩耍了... 当时后背有点发凉。
实不相瞒,就这个问题排查了一段时间,熬到了很晚。最终折腾了一番,经过以下这种方式解决了,顺便分享给小伙伴们,你们若是遇到了这个问题,能够参考着解决一下。
由于从控制台的报错能够很明显的看出,确定是字符编码相关的问题致使的,并且都指向了ascii
这种编码方式。
因而将JDK的源码从根目录导入了Vs Code,而后全目录查找encoding ascii
相关的内容,看看有没有什么端倪,结果发现
jdk源码根目录/make/common/SetupJavaCompilers.gmk
文件中有两处指定了ascii
相关的编码方式:
因而尝试将这两处-encoding ascii
的均替换成-encoding utf-8
:
而后再次执行make images
编译,编译顺利经过!
至此大功告成!
这样后面不论是阅读、调试仍是定制JDK
源码都很是方便了。
后记:这篇文章在开源项目: https://github.com/hansonwang99/JavaCollection 中也已经收录了,包含自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中...
天天进步一点点
慢一点才能更快