你每天写的 Java 代码都需要 JDK 的支持,都要跑在 JVM 上,难道你就不好奇 JDK 长什么样子吗。好奇,就来编译并实现一个自己的 JDK 吧。

本次编译环境 macOS 10.12,编译的是 JDK 11 版本。

安装 OpenJDK 11

编译 OpenJDK 需要先在机器上安装 OpenJDK 10 或者 OpenJDK 11,作为 Boot JDK。

先安装 openJDK 11 编译需要,可以到 adoptopenjdk 网站去下载。

pkg 格式安装

进入页面 https://adoptopenjdk.net/index.html?variant=openjdk11&jvmVariant=hotspot 直接下载下载,然后双击就可以完成安装了。

tar.gz 格式安装

1、进入页面 https://adoptopenjdk.net/installation.html?variant=openjdk11&jvmVariant=hotspot#x64_mac-jdk 下载 tar.gz 包

2、解压

tar -xf OpenJDK11U-jdk_x64_mac_hotspot_11.0.5_10.tar.gz

解压后是一个 macOS 包,可通过右键->显示包内容查看里面的文件。

3、加入环境变量 PATH 中,当然如果你使用其他版本的 JDK 作为开发使用,请忽略这一步。

export PATH=$PWD/jdk-11.0.5+10/Contents/Home/bin:$PATH

4、macOS JDK 默认目录在/Library/Java/JavaVirtualMachines,把第 2 步解压的内容放到此目录下,之后编译的过程中会在这个目录下查找 JDK 10 或 JDK 11。

下面是我本地的目录结构,有 7 、8、11 这三个版本,开发时候还是默认使用 8 的。

安装 xcode

实际上我们需要的不是 xcode,而是 LLVM 的编译命令 clang。当然你可以单独安装 LLVM,但限于此篇是写给 Java 开发者的,安装 xcode 是最简单的版本。

我本地是很早之前安装的 xcode 8.1,编译起来是没问题的,如果你用的是比较新的版本,应该也不会出现什么问题,可以亲自试一试。

开始编译

1、下载 OpenJDK 11 源码

OpenJDK 的源码放在了网站 http://hg.openjdk.java.net/ 上,我们要下载的 JDK11 目录在 http://hg.openjdk.java.net/jdk-updates/jdk11u/。

进入页面后,先点击左侧的 browse,再选择一种压缩格式下载。

当然还可以用 hg 命令 clone 到本地,使用 hg 需要安装 mercurial,如果网速不好或者不稳定,建议不要使用这种方式。

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

2、解压源码包

将你刚刚下载的压缩包解压,请解压到一个全英文目录下,不要使用中文,减少编译时带来的麻烦。

3、configure

进入上一步解压后的目录,执行如下命令。

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

执行这个命令的前提是我已经将 OpenJDK 11 放到了 /Library/Java/JavaVirtualMachines目录下。

如果不放到这个目录下,也是可以的,需要额外指定参数

--with-boot-jdk=OpenJDK 目录

如果出现如下输出,说明这一步就正常了。

4、make

正式开始编译了,使用 make 命令即可。

make

首次编译会比较慢,我的是 MacBook Pro i5 8G 的那款,大概编译了 10 几分钟吧。当出现如下输出的时候,说明编译成功。

Finished building target 'default (exploded-image)' in configuration 'macosx-x86_64-normal-server-slowdebug'

编译好之后,会在当前目录出现 build 目录,进去之后,看到有个 macosx-x86_64-normal-server-slowdebug 就是最终的目录。

IDEA 中配置使用编译好的 JDK

1、打开 IDEA ,找到 File->Project Structure。

2、添加一个 JDK

3、选择上面源码编译好的 jdk

4、最后启动项目的时候指定这个 JDK 就可以了。

用 CLion 调试

1、打开 CLion ,导入项目,选择下载的源码所在位置的 src 目录。

2、配置 Debug Configurations,选择 Executable 为编译好的 java 可执行程序,在 bin 目录下,并且移除 Build 设置。

Program arguments 设置为 -version,也可以设置其他的。设置为 -version 的意思是 java -version。

3、最后在源码中打个断点,比如 jni.cpp 或 thread.cpp 中,然后点击 debug ,就可以调试啦。

打造自己的 JDK

标题说的有点儿悬,打造自己的 JDK 哪儿有那么容易,况且还确实没那个实力。这里就是介绍一种思路,比如有些时候,我们调试 Java 代码最后发现走到了 JVM 层,这种情况下,我们就跟不进去了。执行到 JVM 层之后,里面的各种变量是怎么变换的我们就不知道了。这时候,我们找到 JVM 对应的代码稍微改一下,比如加个 printf 输出一下参数值就可以清晰的看出来了。

修改 JDK 代码

我在打开的 CLion 中找到了 java.c 文件的 JavaMain(void * _args) 方法,在里面加了一行打印代码,就勉强算实现了自己的 JDK 吧(微笑脸)。

万里长征第一步嘛,别的不重要,留下脚印儿才是关键。

printf("古时的风筝 JDK \n");

重新编译修改后的源码

修改之后,在终端中进入到源码目录的根目录,然后执行 make 命令。

因为之前已经编译过了,所以再次执行 make 是进行的增量编译,所以速度很快。

好了,见证奇迹的时刻到了

我们之前已经在 IDEA 中添加了编译好的 JDK,并且指定给了一个项目。仅为测试,代码如下。

public static void main(String[] args){
System.out.println("hello jvm");
}

当我们运行这个项目的时候,如果是平常的 JDK,会在控制台输出 hello jvm ,对不对。

可是,现在指定的不是平常的 JDK ,是被我加持过的 JDK 。

开始运行,输出的结果如下,看到没,刚刚加上的那行代码起作用了。

风筝说

真正能做到 JDK 定制开发的人并不多,我也完全没这个实力。但是每个 Java 开发者都编译一下 JDK 源码,翻一翻代码还是很有必要的。毕竟,我们每天写的代码都需要 JDK 的支持,都要跑在 JVM 上,我们就不好奇它们长成什么模样吗。

另外,这也可能为我们日常解决问题提供一种思路。有人说,最好的老师就是搜索引擎,大多数情况下是没错,但有的时候最好的方式往往就是看一眼源码。

为什么有的人解决问题的速度快,有些看似不能解决的问题放到大牛手里就能很快解决。有时候就是解决问题的维度不一样,人家是在三维的世界里,你却一直在二维的平面里转圈圈,比方说遇到程序问题,只能分析 Java 层面的问题这就是二维,进到 JDK、JVM 源码那就是进到的三维。维度高了,角度变了,解决问题的可能性和方式也就多了。这就好比三体里高等文明利用二向箔进行打击,完全不在一个体量下。

赶紧行动吧,编译一个你自己的 JDK。

给个推荐再走不迟

写 Java 这么久了,来编译个 JDK 玩玩儿吧的更多相关文章

  1. 问题006:为什么用java.exe执行编译的类文件的时候,不这样写java Welcome.class

    为什么用java.exe执行编译的类文件的时候,不这样写java Welcome.class 是因为java虚拟机调用Welcome的时候,已经替我们增减了.class,如果你还要写java Welc ...

  2. myeclipse 写java代码提示 dead code 原因

    经常使用MyEclipse要么Eclipse编辑写java程序猿代码.您可能经常会遇到一个黄色警戒线:dead code:一般程序猿遇到这些问题都会置之不理,反正也不影响程序的编译运行.对,这不是bu ...

  3. 【转】介绍Jython,第一部分:轻轻松松写JAVA程序

    本文转自:http://www.ibm.com/developerworks/cn/education/java/j-jython1/index.html 关于本教程 本教程介绍哪些内容? 这个两部分 ...

  4. Lombok : 让你写 Java代码像C#一样爽

    前言 我曾经是一名 .Net 开发,如今的我是一名 Java 开发者.在我享受着 Java 成熟的生态时,我常常怀念 c# 简洁的语法:自动属性.类型推断.自动初始化器 .... 鱼,我所欲也,熊掌亦 ...

  5. 手写JAVA虚拟机(二)——实现java命令行

    查看手写JAVA虚拟机系列可以进我的博客园主页查看. 我们知道,我们编译.java并运行.class文件时,需要一些java命令,如最简单的helloworld程序. 这里的程序最好不要加包名,因为加 ...

  6. 手写JAVA虚拟机(三)——搜索class文件并读出内容

    查看手写JAVA虚拟机系列可以进我的博客园主页查看. 前面我们介绍了准备工作以及命令行的编写.既然我们的任务实现命令行中的java命令,同时我们知道java命令是将class文件(字节码)转换成机器码 ...

  7. Java逆向武器库_反编译工具

    1.反编译工具之_jd-gui 官网下载地址:http://java-decompiler.github.io/#jd-gui-download 使用: 下载后解压直接使用即可. jd-gui的优势是 ...

  8. [JVM] - 一份<自己动手写Java虚拟机>的测试版

    go语言下载 配置GOROOT(一般是自动的),配置GOPATH(如果想自己改的话) 参照<自己动手写Java虚拟机> > 第一章 指令集和解释器 生成了ch01.exe文件 这里还 ...

  9. 关于JS里面写JAVA代码的问题

    最近做项目需要在JS脚本里面调用一个JAVA的函数得到数据,在网上查了很久,发现JS脚本里面不能写JAVA函数.只能把JS脚本里面的代码写进JSP文件里面的<script>标签内,然后写J ...

随机推荐

  1. codeforces1249-div3

    A B C 等比数列的性质,前面的i项的和,不会超过第i+1项 D 有若干个区间,要求每一个点被区间覆盖的次数不能超过k个.问移除的最少的区间的数目. 贪心: 若某个点被覆盖了k次以上,那么肯定是移除 ...

  2. 实现三个div,固定左右两边的div宽为200,中间的div宽度自适应的四种方法

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. Python--day69--ORM的F查询和Q查询

    F查询和Q查询 F查询 在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较.如果我们要对两个字段的值做比较,那该怎么做呢? Django 提供 F() 来做这样的比较.F() 的实例可 ...

  4. laravel .env 文件的使用

    转载地址  http://www.cnblogs.com/Eden-cola/p/DotEnv-in-lumen.html umen 是 laravel 的衍生品,核心功能的使用和 laravel 都 ...

  5. pytorch nn.LSTM()参数详解

    输入数据格式:input(seq_len, batch, input_size)h0(num_layers * num_directions, batch, hidden_size)c0(num_la ...

  6. Python--day39--进程池的回调函数callback

    运行结果:

  7. Springboot学习笔记(一)—— 安装

    springboot越来越流行了,相比较于springMVC,springboot采用了一种约定大于配置的理念,可以一键安装,一键运行,一键部署,内置tomcat,省去了一大堆配置的时间,并且,spr ...

  8. Linux 内核 启动时间

    为见到 PCI 如何工作的, 我们从系统启动开始, 因为那是设备被配置的时候. 当一个 PCI 设备上电时, 硬件保持非激活. 换句话说, 设备只响应配置交易. 在上电时, 设备没有内存并且没有 I/ ...

  9. mysql常用基础语句学习

    常用sql语句 查询: SELECT 列名(或者*,表示所有列) FROM 表名 WHERE 筛选条件; FROM 表名:顾名思义,就是从表名指定的这张表格中: WHERE 筛选条件:意思是" ...

  10. JavaScript实现版本号比较

    /* * JavaScript实现版本号比较 * 传入两个字符串,当前版本号:curV:比较版本号:reqV * 调用方法举例:Version('5.12.3','5.12.2'),将返回true * ...