vlambda博客
学习文章列表

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

前言

这篇文章的原因主要有以下三个方面:

  • 第一个方面是因为在前几篇文章中,在讲类加载器的初始化过程、类加载器的启动时机这两块内容时穿插了不少的HotSpot源码,肯定会有小伙伴想自己跟一下HotSpot源码,所以在这专门写一篇HotSpot源码阅读环境的文章给需要的小伙伴

  • 其次在之后的文章中会讲对象的创建过程、垃圾回收算法、GC安全点和安全区域、垃圾回收器等等一系列的内容中,难免会扯到HotSpot源码中去,所以还不如现在就把源码阅读环境搭建好,到时候跟着文章的思路一步步的跟下来呢

  • 最后一个方面同时也是一个问题:小伙伴真的对JVM不感兴趣吗?

那么话不多说,开始准备搭建吧!!!

准备工作

第一个环节:软件准备

我这里针对的是我自己的环境,您可以根据你自己的环境,去搭建,道理都是一样的。

  • OS:Mac系统

  • IDE:Clion

  • 源码:OpenJDK8

第二个环节:环境搭建

IDE Clion

首先你得安装一个Clion,因为HotSpot虚拟机是C++写的,链接在这,请点我

测试IDE Clion可用

安装完Clion后,随便新建一个项目,测试一下,确保Clion是正常能使用的

我们点击create之后,发现报下面截图中的错,关键在missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun这行错误代码,百度一下说是缺少Command Line Tools工具,那么就下载一下这个工具

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

安装Command Line Tools

进入苹果的开发者网站:请点这进入,进行下载,下载后安装pkg文件即可

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

安装完之后,打开Clion,发现原先报错的地方都置灰了,说明已经修复了

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

运行main函数后,也正常输出了,说明Clion已经正确安装

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

下载openjdk源码

IDE准备好了,下一步就需要准备openjdk源码了,由于官网下载网速太慢,容易文件下不全,我就直接在网上找到了一个社区维护的openjdk-jdk8u版本

https://github.com/AdoptOpenJDK/openjdk-jdk8u

第三个环节:编译源码

下载完最新的openjdk源码之后,就需要对它进行编译

准备编译工具

// 加速编译
brew install ccache
// 字体引擎,编译过程中会被依赖到
brew install freetype
brew install autoconf
// 分布式版本控制
brew install mercurial

配置BOOT_JDK很重要,不然编译会报出各种千奇百怪的问题

编译之前需要配置一个称作BOOT_JDK的东西,其版本要比编译的版本低一级,即编译OpenJDK8,就需要安装JDK7作为环境OpenJDK7或OracleJDK7均可

安装compiledb很重要,解决头文件费了将近两天,最后发现安装这玩意就好了,我吐血了

其实这一步的操作的原因是为了解决在编译openjdk源码的时候很多头文件找不到的问题

安装要求:需要python3+,brew install python3

然后需要安装pip,安装指令为:curl https://bootstrap.pypa.io/get-pip.py | python3

最后通过pip安装compiledb即可,指令为:pip install compiledb

配置环境变量

vi ~/.bash_profile

在.bash_profile文件底部插入下面的配置信息:

# 设定语言选项,必须设置
export LANG=C
# Mac平台,C编译器不再是GCC,而是clang
export CC=clang
export CXX=clang++
export CXXFLAGS=-stdlib=libc++
# 是否使用clang,如果使用的是GCC编译,该选项应该设置为false
export USE_CLANG=true
# 跳过clang的一些严格的语法检查,不然会将N多的警告作为Error
export COMPILER_WARNINGS_FATAL=false
# 链接时使用的参数
export LFLAGS='-Xlinker -lstdc++'
# 使用64位数据模型
export LP64=1
# 告诉编译平台是64位,不然会按照32位来编译
export ARCH_DATA_MODEL=64
# 允许自动下载依赖
export ALLOW_DOWNLOADS=true
# 并行编译的线程数,编译时长,为了不影响其他工作,可以选择2
export HOTSPOT_BUILD_JOBS=4
export PARALLEL_COMPILE_JOBS=2 #ALT_PARALLEL_COMPILE_JOBS=2
# 是否跳过与先前版本的比较
export SKIP_COMPARE_IMAGES=true
# 是否使用预编译头文件,加快编译速度
export USE_PRECOMPILED_HEADER=true
# 是否使用增量编译
export INCREMENTAL_BUILD=true
# 编译内容
export BUILD_LANGTOOL=true
export BUILD_JAXP=true
export BUILD_JAXWS=true
export BUILD_CORBA=true
export BUILD_HOTSPOT=true
export BUILD_JDK=true
# 编译版本
export SKIP_DEBUG_BUILD=true
export SKIP_FASTDEBUG_BULID=false
export DEBUG_NAME=debug
# 避开javaws和浏览器Java插件之类部分的build
export BUILD_DEPLOY=false
export BUILD_INSTALL=false

# 最后需要干掉这两个环境变量(如果你配置过),不然会发生诡异的事件
unset JAVA_HOME
unset CLASSPATH

使环境变量生效:

source ~/.bash_profile

执行配置文件校验命令

进入下载的openjdk目录,执行配置文件校验命令注意:一定要切换到BOOT_JDK的版本下执行

sh configure --with-freetype-include=/usr/local/include/freetype2 --with-freetype-lib=/usr/local/lib/ --disable-zip-debug-info --disable-debug-symbols --with-debug-level=slowdebug --with-target-bits=64 --with-jvm-variants=server

执行配置文件报错案例

  1. 执行之后报下图中的错,意思就是检测不到Xcode的版本

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

解决办法:去苹果的开发者网站:请点这进入下载mac系统版本对应的Xcode,下载完Xcode之后记得执行下面的命令,不然还是会报上图中的错

sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

  1. 报Xcode 4 is required to build JDK 8

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

解决办法:找到configure文件并打开vim common/autoconf/generated-configure.sh,找到判断版本的地方,将这一段全部注释掉.

    #Fail-fast: verify we're building on Xcode 4, we cannot build with Xcode 5 or later
XCODE_VERSION=`$XCODEBUILD -version | grep '
^Xcode ' | sed 's/Xcode //'`XC_VERSION_PARTS=( ${XCODE_VERSION//./ } )
if test ! "${XC_VERSION_PARTS[0]}" = "4"; then
as_fn_error $? "Xcode 4 is required to build JDK 8, the version found was $XCODE_VERSION. Use --with-xcode-path to specify the location of Xcode 4 or make Xcode 4 active by using xcode-select." "$LINENO" 5
fi
#
  1. A gcc compiler is required的问题

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

解决办法:找到下面这段校验代码并注释掉,注意有两处,代码是相同的,根据下图中的标志寻找

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

    if test $? -ne 0; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required GCC compiler." >&5
$as_echo "$as_me: The $COMPILER_NAME compiler (located as $COMPILER) does not seem to be the required GCC compiler." >&6;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: The result from running with --version was: \"$COMPILER_VERSION_TEST\"" >&5
$as_echo "$as_me: The result from running with --version was: \"$COMPILER_VERSION_TEST\"" >&6;}
as_fn_error $? "GCC compiler is required. Try setting --with-tools-dir." "$LINENO" 5
fi

出现下图的样子,证明校验完成,注意:要确认Boot JDK的版本

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

开始编译每一次编译都要耗费很多的时间,所以建议把文中的错误都改了之后,再进行编译

执行compiledb make WARNINGS_ARE_ERRORS="" CONF=macosx-x86_64-normal-server-slowdebug all命令进行编译

如果出错的话,一定要按照下面的步骤重新编译:

  1. compiledb make CONF=macosx-x86_64-normal-server-slowdebug clean

  2. compiledb make WARNINGS_ARE_ERRORS="" CONF=macosx-x86_64-normal-server-slowdebug all

compiledb make WARNINGS_ARE_ERRORS="" CONF=macosx-x86_64-normal-server-slowdebug all

编译的报错案例

  1. fatal error: 'iostream' file not found的问题:解决方案:设置环境变量

NEW_INCLUDE=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
export CFLAGS=-I$NEW_INCLUDE
export CXXFLAGS=-I$NEW_INCLUDE
  1. library not found for -lstdc++的问题:解决方案:按照如下操作即可。

  • 克隆一个工具:git clone https://github.com/quantum6/xcode-missing-libstdcpp(注意:该文件的内容会软连接到Xcode中,所以该目录不能删除)

  • 然后进入该工具执行sh install.sh

  • 配置环境变量

NEW_LIB=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib
export LDFLAGS="-L${NEW_LIB}"
export LIBRARY_PATH=$NEW_LIB:$LIBRARY_PATH
  1. symbol(s) not found for architecture x86_64的问题:解决方案:找到文件jdk/src/macosx/native/sun/osxapp/ThreadUtilities.m,然后把inline void attachCurrentThread(void** env) 改为static inline void attachCurrentThread(void** env)

Undefined symbols for architecture x86_64:
"_attachCurrentThread", referenced from:
+[ThreadUtilities getJNIEnv] in ThreadUtilities.o
+[ThreadUtilities getJNIEnvUncached] in ThreadUtilities.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. clang: error: unknown argument: '-fpch-deps'的问题,解决方案:编辑hotspot/make/bsd/makefiles/gcc.make 注释以下代码

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

ifeq ($(USE_CLANG),)
ifneq ($(CC_VER_MAJOR), 2)
DEPFLAGS += -fpch-deps
endif
endif

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. error: invalid argument '-std=gnu++98' not allowed with 'C'的问题,解决方案:编辑common/autoconf/generated-configure.sh,搜索-std=gnu++98这个字串并注释

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. 解决在slowdebug模式下编译之后崩溃的问题:在slowdebug模式下编译完成之后,执行java -version后会有JVM奔溃的错误。找到文件hotspot/src/share/vm/runtime/perfMemory.cpp文件。注释掉如下内容:

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. implicit declaration of function 'VerifyFixClassname' is invalid in C99的问题:解决方案:include头文件或者直接外部声明最好include头文件,找不到再直接声明

extern jboolean VerifyFixClassname(char* name);
extern jboolean VerifyClassname(char* name, jboolean allowArrayClass);

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. implicit declaration of function 'pthread_main_np' is invalid in C99的问题:和上面一样最好include头文件,找不到再直接声明

extern jboolean pthread_main_np();

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. implicitly declaring library function 'strchr' with type 'char *(const char *, int)'的问题:和上面一样最好include头文件,找不到再直接声明

#include <stdio.h>
#include <string.h>

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. implicit declaration of function 'time' is invalid in C99的问题:和上面一样最好include头文件,找不到再直接声明

#include <time.h>

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

  1. implicit declaration of function 'JVM_ActiveProcessorCount' is invalid in C99的问题:这里直接改成jint ncpus = 4;

jint ncpus = 4;

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

第四个环节:向Clion中导入源码并配置

如果出现如下图所示的界面,说明已经编译成功,在编译后有可能在最后有大量的No such file or directory的警告。这个不用担心,下一步就是向Clion中导入编译后的源码

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

由于使用了compiledb包装编译OpenJDK源码。所以编译完成之后我们在源码根目录可以看到多了一个compile_commands.json文件。我们的工程导入则依赖这个json文件。

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

打开Clion,然后点击Open打开上述compile_commands.json文件,并在弹出的对话框中选择Open as Project。

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

等待导入成功后,进入Clion的preferences->Build,Execution,Deployment->Custom Build Targets,在改配置页面点击Add Target配置自定义的Build Targets

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

name自己自行输入,Toolchain选择Default

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

新建Tool,一个是make_jdk8,一个是clean_jdk8,(名字都自行取一个就行)

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

make_jdk8: 其中arguments填写:CONF=macosx-x86_64-normal-server-slowdebug,Working directory则添加源码的根目录。

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

clean_jdk8: 其中arguments填写:CONF=macosx-x86_64-normal-server-slowdebug clean,Working directory则添加源码的根目录。

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

然后再Clutom Build Targets完整具体配置,具体配置如下:

从零开始学JVM系列(五):搭建HotSpot源码阅读环境

接着在Clion主页面点击Add Configuration,新增配置Run/Debug Configurations。

第五个环节:开始调试

在完成所有配置后,我们就可以开始调试代码了。首先在jni.cpp的create_vm中添加打一个断点。

到此debug的调试设置好了,HotSpot源码阅读环境也就搭建完了,希望大家都能成功!!!

絮叨