对于将一些开源代码动手改改就变成自己的代码事情,屡见不鲜,比如有一家公司就将nginx改了改取了个名字叫:tengine,又有一家公司将zookeeper改了改叫taokeeper,还有一家公司将protobuf改了改暂时还没发布其名字。
最近看到一款开源jvm叫:taobaojvm,据说其性能比openjdk高出30%,基于openjdk修改而来,如此高大上的性能提升我暂时做不到,但是编译个jvm修改个名字,调调显示啥的,对于我来说还不是问题,于是我也觉得自己改是时候显示身手了,我这么牛逼怎么能这样埋没了,编译个自己的jvm,别人叫jvm我叫Bvm,别人叫jdk我叫Bdk,接下来我开始编译自己的bdk和bvm。
首先下载openjdk这是必不可少的,想起自己上次编译都是2年前了,于是重新去下载了openjdk8,其文件结构如下:
稍微解释文件夹的作用:
这是类库存在的地方。几乎所有的内容都是Java(本地方法会使用一些C代码)。这是深入学习OpenJDK源码的一个非常好的起点。JDK的类在jdk/src/share/classes目录中。
HotSpot虚拟机——这里面是C/C++和汇编代码(还有一些基于Java的虚拟机开发工具)。这些内容非常高级,如果你并不是一个专业的C/C++开发人员那么这些内容会让人有一点难以入手。
对于那些对编译器和工具开发感兴趣的人而言,可以从这里找到语言和平台工具。大部分是Java和C代码——学习这些内容比学习JDK代码要难,但是对于大多数开发者而言还是可以接受的。
还有一些其他的仓库,但是它们可能没有那么重要或者对大多数开发者而言没什么吸引力,这些仓库包括corba、jaxp和jaxws等内容。
在文件夹下面有个文件叫README,里面说了编译的步骤:
就是这么简单,想到当年编译1.6的时候还各种set和unset,在1.8下就是这么简单,不过在这个过程还是会遇到几个问题,大概描述下:
cc1plus: error: the "stabs" debug format cannot be used with pre-compiled headers [-Werror=deprecate]
因为高版本的gcc不再支持stabs,解决办法:在make命令中加上 DEBUG_BINARIES=true
vim ./hotspot/make/linux/makefiles/gcc.make
其中的Linux是对应的自己的操作系统
jdk1.7在127行,jdk1.8在207行,注释掉:
WARNINGS_ARE_ERRORS = -Werror
如果只是这么一步步的按兵不动的编译出jvm,不符合我高大上的风格,当然要做点修改,修改哪儿?当然是修改名称最简单,我希望看到的是:
java -version
输出:bdk1.8.0等等可以自定义的东西,至少看起来像那么一回事儿吧,于是动手开始改,java是一个可执行文件,而-version是参数,那么意味着必然有一个带main函数的c或者c++文件最后编译生成了java二进制执行文件,这个文件的源码就是java.c,在源码里存在两个java.c分别是:
./hotspot/src/share/tools/launcher/java.c
./ jdk/src/share/bin/java.c
jdk下面的那个java.c虽然存在,但是并没有其用处,最后编译是使用的hotspot里的,打开java.c可以看到main方法:
在这里经过一系列的检查后会新开一个子线程执行 JavaMain方法 :
在 JavaMain方法里面有一段代码,在前面经过一系列的检查后,会来到如下的位置:
这就是显示版本号的地方,果断的找到这个方法:
可以看到当执行java -version的时候是通过jni调用的sun/misc/version这个class,但是源码里面并没有这个类,原因在于这个类是动态编译生成的,在makefile里面定义了模版类:
./jdk/src/share/classes/sun/misc/Version.java.template
也就是说当编译jvm的时候,会根据Version.java.template这个模版动态生成version的java类,于是打开这个模版类:
可以看到在编译过程中源码会根据模版类,动态替换里面的一些值,然后动态生成Version的java类,那么如果要修改版本号显示什么的,都在这里进行操作了,修改完成后再重新编译,生成的就是带自己标签的jvm了。。
比如我的就是这样: