前言

上一篇文章介绍了几种 JVM,接下来,我将以 OpenJDK 8 中的 HotSpot VM 为例,通过分析其源码,探索 JVM 的实现。本篇主要记录调试环境的搭建过程。

由于在 Windows 下编译 JVM 必须使用 Visual Studio,然而本人用惯了 JetBrains 家的 CLion,不想更换 IDE,所以选择在 Linux (CentOS7) 上编译,在 Windows 上使用 CLion 远程调试。

这里需要注意,由于整个操作过程需要安装很多工具,并且编译时还将产生大量的临时文件,因此,在开始编译前必须确保有足够的磁盘空间(最好大于20G)。

一、准备源码

CentOS 中执行如下命令:

# 下载源码包
wget https://download.java.net/openjdk/jdk8u41/ri/openjdk-8u41-src-b04-14_jan_2020.zip

# 如果没有安装 unzip,先安装
yum install -y unzip

# 解压
unzip openjdk-8u41-src-b04-14_jan_2020.zip

二、安装 "Bootstrap JDK"

OpenJDK 的编译除了依赖 C/C++ 编译器之外,还依赖一个 Java 编译器。这是因为 OpenJDK 的很多模块都是用 Java 写的,编译这部分代码就需要用到另一个 JDK。官方称这个 JDK 为 “Bootstrap JDK”, 它的版本应当低于需要编译的目标 JDK 的版本。

编译 OpenJDK 8 需要使用 Update 7 或更高版本的 JDK 7 版本。参考源码根目录下的 “README-builds.html” 文件。

CentOS 中执行如下命令:

# 卸载已安装的JDK
yum list installed | grep jdk
rpm -qa | grep jdk
yum remove -y xxxx

# 确保卸载成功
java -version

# 下载jdk-7u80,这里选择从华为镜像站下载
wget https://repo.huaweicloud.com/java/jdk/7u80-b15/jdk-7u80-linux-x64.tar.gz

# 解压到指定目录
tar -zxf jdk-7u80-linux-x64.tar.gz -C /usr/local/java/

# 配置环境变量
vim /etc/profile
 # 追加如下内容
    export JAVA_HOME=/usr/local/java/jdk1.7.0_80
    export CLASSPATH=$CLASSPATH:$JAVA_HOME/lib/
    export PATH=$PATH:$JAVA_HOME/bin
source /etc/profile

# 确保安装成功
java -version

完成后如图所示:

三、配置编译环境

CentOS 中执行如下命令:

# 安装编译所需工具
yum install -y gcc gcc-c++ make libXtst-devel libXt-devel libXrender-devel cups-devel freetype-devel alsa-lib-devel fontconfig-devel

# 进入源码目录
cd openjdk/

# 确保configure脚本拥有可执行权限
chmod +x configure

# 执行configure脚本,看看缺少什么依赖项,根据错误提示安装即可,然后重复执行直到提示成功
./configure --with-debug-level=slowdebug --enable-debug-symbols --disable-zip-debug-info

# 参数说明
# --with-debug-level=slowdebug 设置编译级别为slowdebug,将会输出较多的调试信息
# --enable-debug-symbols 启用调试符号,将会生成调试信息文件
# --disable-zip-debug-info 禁用调试信息压缩,否则,调试信息默认会被压缩成"libjvm.diz"文件,调试时只能看到汇编代码,不能跟进源码

完成后如图所示:

四、编译与测试

CentOS 中执行如下命令:

# 编译(这里启动6条编译线程以加快编译速度)
make JOBS=6

# 测试
./build/linux-x86_64-normal-server-release/jdk/bin/java -version

# 确保"libjvm.debuginfo"文件存在,否则调试时将不能跟进源码
ls ./build/linux-x86_64-normal-server-release/jdk/lib/amd64/server/

如果 Linux 内核版本为 4+,编译时将出现 “This OS is not supported” 的报错。解决办法是修改源码目录下的 “./hotspot/make/linux/MakeFile” 文件,找到 SUPPORTED_OS_VERSION 变量定义的地方,在后面追加 “4%”,如下图所示。

如果一切顺利,将会看到如图所示信息:

至此,我们已经完成了 JDK 的编译。

五、安装 CMake 和 GDB

为了在本地使用 CLion 进行远程调试,需要在服务端安装与本地版本相兼容的 CMake 和 GDB。

由于从 yum 源安装的版本较低,因此这里选择编译安装。

CentOS 中执行如下命令:

# 卸载已有的cmake和gdb
yum remove -y cmake gdb

# 下载cmake3.14.5(使用华为镜像站)
wget https://mirrors4.tuna.tsinghua.edu.cn/pkgsrc/distfiles/cmake-3.14.5.tar.gz

# 解压
tar -zxf cmake-3.14.5.tar.gz

# 进入cmake目录,执行编译安装
cd cmake-3.14.5
./bootstrap && make && make install

# 为cmake命令创建软链接
ln -s /usr/local/bin/cmake /usr/bin/cmake

# 验证是否安装成功
cmake -version

# 回到上一级目录,准备安装gdb
cd ..

# 下载gdb8.1(使用华为镜像站)
wget https://mirrors4.tuna.tsinghua.edu.cn/pkgsrc/distfiles/gdb-8.1.tar.gz

# 解压
tar -zxf gdb-8.1.tar.gz

# 安装编译所需工具
yum install -y texinfo

# 进入gdb目录,执行编译安装
cd gdb-8.1
./configure && make && make install

# 为gdb命令创建软链接
ln -s /usr/local/bin/gdb /usr/bin/gdb

# 验证是否安装成功
gdb -ver

五、准备远程调试

在 CLion 中创建一个空项目,推荐 Language standard 选择 C++11。

配置工具链,如图所示。

配置 SFTP 连接,用于连接到远程主机。

配置路径映射,用于同步两端的代码。

配置排除路径,排除本地的 cmake 输出路径。

将远程主机的代码同步到本地。在 Project 面板中点右键,在弹出的菜单中选择 Deployment -> Download from,然后点击目标 Server,等待下载。如图所示。

同步的过程比较耗时,主要是因为 CLion 需要给每一个文件建立映射关系。同步完成后的效果如下。

接下来需要在 CMakeLists.txt 文件中完成项目模型的配置。

CMakeLists.txt 中的一些基本配置可以通过 “New CMake Project from Sources” 的方式让 CLion 自动完成。不过 CLion 仅仅是把项目文件注册进来,并没有正确配置依赖关系和宏定义,当我们打开代码时会发现处处爆红。

为了解决这些爆红,我花了两个晚上,经反复尝试,终于得到了一个看似使代码不再爆红的 CMakeLists.txt 文件。

附上 CMakeLists.txt 文件的链接:https://github.com/zhangyongheng/jdkbuild/blob/master/openjdk8/CMakeLists.txt

项目模型配置好了之后,接下来就可以开始远程调试了。

六、开始远程调试

首先在远程主机上开启 GDB 服务,命令如下:

# 开启GDB服务,这里指定端口号为8899,后面跟上要调试的java命令
gdbserver :8899 /root/openjdk/build/linux-x86_64-normal-server-release/jdk/bin/java -version

# 如果没有安装gdbserver,先安装
yum install -y gdb-gdbserver

打开 CLion 的 "Run/Debug Configuration" 对话框,添加一个 "GDB Remote Debug",按照如图所示进行配置。其中,"target remote args" 是远程 GDB 服务的 IP 地址和端口号,"Path mappings" 是远程项目根路径与本地项目根路径间的映射关系。

打开 "jdk/src/share/bin/java.c" 文件,找到 JavaMain() 函数,它是 Hotspot 虚拟机的执行入口,我们可以在这个方法内打上断点。

点击 "Debug" 开始调试,在 ”GDB“ 标签页中可以查看到 GDB 日志,我们还可以在这里通过输入 GDB 命令,以命令行的方式执行调试。

从 GDB 日志中可以看到, "libjvm.debuginfo" 文件的载入通常比较耗时,我这里大约需要等待 3 分钟,然后程序启动后会停在断点的位置。

在跟踪调试之前,还需要设置 GDB 对 SIGSEGV 信号的处理方式,忽略调试时的 SIGSEGV 信号。

在 ”GDB“ 标签页下的 "(gdb)" 命令行中输入 "handle SIGSEGV nostop noprint pass",如图所示。

接下来就可以继续跟踪调试了。

调试完成后,远程 GDB 服务将会退出,再次调试需要重新开启 GDB 服务。

JVM 源码分析(二):搭建 JDK 8 源码调试环境(Windows 上使用 CLion)的更多相关文章

  1. Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题

    4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...

  2. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

  3. Tomcat源码分析二:先看看Tomcat的整体架构

    Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Conn ...

  4. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

  5. Vue源码分析(二) : Vue实例挂载

    Vue源码分析(二) : Vue实例挂载 author: @TiffanysBear 实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-wi ...

  6. 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>

    目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...

  7. Docker源码分析(二):Docker Client创建与命令执行

    1. 前言 如今,Docker作为业界领先的轻量级虚拟化容器管理引擎,给全球开发者提供了一种新颖.便捷的软件集成测试与部署之道.在团队开发软件时,Docker可以提供可复用的运行环境.灵活的资源配置. ...

  8. jQuery 源码分析(二) 入口模块

    jQuery返回的对象本质上是一个JavaScript对象,而入口模块则可以保存对应的节点的引用,然后供其它模块操作 我们创建jQuery对象时可以给jQuery传递各种不同的选择器,如下: fals ...

  9. Android笔记--View绘制流程源码分析(二)

    Android笔记--View绘制流程源码分析二 通过上一篇View绘制流程源码分析一可以知晓整个绘制流程之前,在activity启动过程中: Window的建立(activit.attach生成), ...

随机推荐

  1. 1、tensorflow 框架理解

    2020/10/31 参考:https://blog.csdn.net/mzpmzk/article/details/78636127 1. 两大步骤:定义图define the graph, 进行计 ...

  2. Elastic Search 学习之路(三)—— tutorial demo

    一.ElasticSearch tutorial demo example 1. 单机.local.CRUD操作 实现方式: SpringBoot + ElasticSearch 拷贝的小demo,原 ...

  3. Docker(八): 安装ELK

    服务部署发展 传统架构单应用部署 应用程序部署在单节点中,日志资源同样输出到这台单节点物理机的存储介质中. 微服务架构服务部署 以分布式,集群的方式部署应用,应用分别部署在不同的物理机中,日志分别输出 ...

  4. Python高级语法-私有化-私有化理解(4.3.1)

    @ 目录 1.说明 2.代码 关于作者 1.说明 __a(私有):只能在类的内部使用,对象使用,但是子类不允许使用,不能导入到其他包 a(protected):可以在子类使用,可以通过对象访问,不能导 ...

  5. Spring笔记(9) - IOC实现方式详解

    IOC概念 控制反转(Inversion of Control,IOC),是面向对象编程中的一种设计原则,它建议将不需要的职责移出类,让类专注于核心职责,从而提供松散耦合,提高优化软件程序设计.它把传 ...

  6. SpringCloud 的版本问题 (深入)

    学过SpringCloud 的 同学 ,就应该知道,SpringCloud是一个生态 . Spring Cloud是一个由众多独立子项目组成的大型综合项目,每个子项目都维护着自己的发布版本号.Spri ...

  7. 性能监控工具nmon及nmon_analyser的使用

    nmon和nmon_analyser下载地址: http://nmon.sourceforge.net/pmwiki.php?n=Site.Download   使用步骤: 1.nmon 根据系统版本 ...

  8. burpsuite 2020.12.1最新版蓝色版,下载安装破解

    最新版Burpsuite下载破解安装 安装包链接:https://cloud.189.cn/t/qUVzYbuAJzqm(访问码:mw12) 1.首先,我们解压,可以看到解压后的文件夹里有如下文件 2 ...

  9. 关于python面试中的设计模式,搞懂这些就足够了

    1.什么是设计模式? 设计模式是经过总结.优化,对我们经常遇到的一些编程问题的可重用的解决方案.设计模式不同于类或库可直接作用于代码.相反,它更为的高级,是一种必须在特定的情形下实现的方法模版. 2. ...

  10. EF Core扩展工具记录 批量操作 记录修改删除历史 动态linq

      Microsoft.EntityFrameworkCore.UnitOfWork  Microsoft.EntityFrameworkCore的插件,用于支持存储库,工作单元模式以及支持分布式事务 ...