查看: 196|回复: 0

JVM源码分析-JVM源码编译与调试

[复制链接]
发表于 2020-2-16 03:52:48 | 显示全部楼层 |阅读模式
要分析JVM的源码,结合资料直接阅读是一种方式,但是遇到一些想不通的场景,必须要结合调试,查看执行路径以及参数具体的值,才能搞得明确。以是我们先来把JVM的源码进行编译,并能够使用GDB进行调试。
编译情况

本文使用的JDK版本:OpenJDK7,分支b147
下载页面:https://download.java.net/openjdk/jdk7
下载地址:http://download.java.net/openjdk/jdk7/promoted/b147/openjdk-7-fcs-src-b147-27_jun_2011.zip
MD5:c284c89a104f64a95afde3a96138ef0f
其他情况说明:

  • CentOS 7.4 64位
  • gcc version 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
  • GNU Make 3.82
安装依赖
  1. yum -y install gcc gcc-c++ make yum -y install alsa-lib-develyum -y install cups-develyum -y install libX*yum -y install gcc gcc-c++yum -y install libstdc++-staticwget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repoyum -y install ant 
复制代码
编译JVM,需要使用到更早之前一个版本的JDK,比如我们编译的是7,就需要安装OracleJDK6:
下载地址:https://www.oracle.com/java/technologies/javase-java-archive-javase6-downloads.html
http://gcdncs.101.com/v0.1/static/test_mzb/jdk-6u38-linux-x64-rpm.bin
下载:jdk-6u38-linux-x64-rpm.bin
  1. $ sh jdk-6u38-linux-x64-rpm.bin$ sudo rpm -ivh jdk-6u38-linux-amd64.rpm
复制代码
编写编译脚本

解压openjdk:
  1. unzip openjdk-7-fcs-src-b147-27_jun_2011.zip
复制代码
在openjdk目次下添加一个build.sh脚本:
  1. #!/bin/bashexport LANG=C#将一下两项设置为你的BootstrapJDK安装目次export ALT_BOOTDIR=/usr/java/jdk1.6.0_38export ALT_JDK_IMPORT_PATH=/usr/java/jdk1.6.0_38#答应自动下载依赖包export ALLOW_DOWNLOADS=true#使用预编译头文件,以提拔便以速度export USE_PRECOMPILED_HEADER=true#要编译的内容,我只选择了LANGTOOLS、HOTSPOT以及JDKexport BUILD_LANGTOOLS=trueexport BUILD_JAXP=falseexport BUILD_JAXWS=falseexport BUILD_CORBA=falseexport BUILD_HOSTPOT=trueexport BUILD_JDK=true#要编译的版本export SKIP_DEBUG_BUILD=falseexport SKIP_FASTDEBUG_BUILD=trueexport DEBUG_NAME=debug#避免javaws和浏览器Java插件等的buildBUILD_DEPLOY=false#不build安装包BUILD_INSTALL=false#包含全部的调试信息export  ENABLE_FULL_DEBUG_SYMBOLS=1#调试信息是否压缩,如果配置为1,libjvm.debuginfo会被压缩成libjvm.diz,将不能被debug。export  ZIP_DEBUGINFO_FILES=0#用于编译线程数export  HOTSPOT_BUILD_JOBS=3#设置存放编译结果的目次#export ALT_OUTPUTDIR=/root/jvm/outputunset CLASSPATHunset JAVA_HOMEmake sanityDEBUG_BINARIES=true make 2>&1
复制代码
然后执行 sh build.sh 进行编译。如果编译过程中遇到问题,可以查阅下文的编译问题办理的部分。
运行HotSpot

编译成功后,编译的输出默认在openjdk/build目次下。HotSpot的编译输出在openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg目次下。
使用HotSpot提供的命令执行测试
  1. cd build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg# test_gamma是HotSpot提供的一个测试步伐,可以成功执行说明编译成功./test_gamma# 使用hotspot脚本进行GDB调试./hotspot -gdb HelloWorld
复制代码
./hotspot是一个脚本,查阅代码可以看出他做了一些简单的事情,主要是会设置情况变量:
  1. JAVA_HOME=/usr/java/jdk1.6.0_38LD_LIBRARY_PATH=/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64
复制代码
然后天生GDB参数脚本,并运行GDB命令:
  1. gdb -x /tmp/hsl.26037
复制代码
/tmp/hsl.26037是脚本天生的gdb参数,我们可以看看都设置了什么:
  1. cd /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmghandle SIGUSR1 nostop noprinthandle SIGUSR2 nostop noprintset args HelloWorld file /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/gammadirectory /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg# Get us to a point where we can set breakpoints in libjvm.sobreak InitializeJVMrun# Stop in InitializeJVMdelete 1# We can now set breakpoints wherever we like
复制代码
可以看出设置了源码目次,设置了一个默认断点。分析了hotspot脚本后,我们可以根据需要用最原始的方式来启动hotspot和gdb来实现更复杂的调试需求,比如长途调试。
直接执行的方式:
  1. JAVA_HOME=/usr/java/jdk1.6.0_38 LD_LIBRARY_PATH="/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64" ./gamma HelloWorld
复制代码
GDB调试:
  1. JAVA_HOME=/usr/java/jdk1.6.0_38 LD_LIBRARY_PATH="/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64" gdb ./gamma HelloWorld
复制代码
GDB长途调试:
  1. JAVA_HOME=/usr/java/jdk1.6.0_38 LD_LIBRARY_PATH="/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg:/usr/java/jdk1.6.0_38/jre/lib/amd64" gdbserver :8011 ./gamma HelloWorld
复制代码
Hotspot代码调试技巧

GDB的使用和技巧这里就不说了,我自己也是遇到问题现查资料的,这里列几个常用的和HotSpot有关的调试技巧。
怎样打印HotSpot内部符号对象Symbol对应的字符串?
Symbol是一个非经常见的类,全部的符号引用对应的字符串,都会用Symbol来表示,比如类名、方法名、方法签名等等,可以用一下方法输出Symbol对应字符串:
复制代码
怎样打印KlassHandle对应的类名?
  1. p Klass::cast(current_klass.obj())->external_name()
复制代码
添加加载特定类时的断点
  1. break ClassFileParser::parseClassFile if strncmp(class_name._body, "XXX", 3) == 0break SystemDictionary::load_instance_class if strncmp(class_name._body, "XXX", 3) == 0
复制代码
遇到的问题
  1. BootstrapJDK一开始设置为JDK8会失败,要改为JDK6
复制代码
  1. #错误echo "*** This OS is not supported:" `uname -a`; exit 1;#办理sudo vim openjdk/hotspot/make/linux/Makefile注释掉以下三行238 #ifeq ($(DISABLE_HOTSPOT_OS_VERSION_CHECK)$(EMPTY_IF_NOT_SUPPORTED),)239 # $(QUIETLY) >&2 echo "*** This OS is not supported:" `uname -a`; exit 1;240 #endif
复制代码
  1. #错误error:"__LEAF"redefined [-Werror]#办理ubuntu12的glibc比力新,在linux的头文件cdefs.h里,有个__LEAF的宏,这个和hotspot/src/share/vm/runtime/interfaceSupport.hpp这个头文件中的宏界说有冲突,我们在428行下面增长一个#undef __LEAF如下:428 // LEAF routines do not lock, GC or throw exceptions#ifdef __LEAF#undef __LEAF#define __LEAF(result_type, header)                                  \  TRACE_CALL(result_type, header)                                    \  debug_only(NoHandleMark __hm;)                                     \  /* begin of body */#endif
复制代码
  1. #错误Error:/openjdk/hotspot/src/share/vm/oops/constantPoolOop.cpp:272:39: error: converting 'false' to pointer type 'methodOop' [-Werror=conversion-null]#办理vi hotspot/src/share/vm/oops/constantPoolOop.cpp将272行 return false  改为 return NULL
复制代码
  1. #错误/usr/openjdk/hotspot/src/share/vm/opto/loopnode.cpp:896:49: error: converting 'false' to pointer type 'Node*' [-Werror=conversion-null]#办理vi hotspot/src/share/vm/opto/loopnode.cpp将896行 return false  改为 return NULL
复制代码
  1. #错误Using java runtime at: /usr/lib/jvm/java-1.6.0/jre./gamma: relocation error: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.41.x86_64/jre/lib/amd64/libjava.so: symbol JVM_FindClassFromCaller, version SUNWprivate_1.1 not defined in file libjvm.so with link time reference#办理这里是有一个坑的,为了避免大家踩坑,请提前安装好Oracle JDK 1.6。# ALT_BOOTDIR 用到的是 OpenJDK 1.6.0 会有此报错, OpenJDK 的bug,需要使用 Oracle JDK# 见到雷同下方的报错了,恭喜童鞋您入坑了# ./gamma: relocation error: /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.41.x86_64/jre/lib/amd64/libjava.so: symbol JVM_FindClassFromCaller, version SUNWprivate_1.1 not defined in file libjvm.so with link time reference下载传送门大概需要登陆Oracle,没有帐号的童鞋请注册一下。笔者下载安装的是jdk-6u38-linux-x64-rpm.bin。如果上一个传送门失效,请继续传送!找到此页面上的Java SE 6进入传送哦~如果这个传送也失效了(T_T),那接着传送,拉到页面最下方,找到Java Archive栏,点击右侧DOWNLOAD按钮自行传送。再不行就只能找baidu了~~~# 对下载到的bin动动手脚(不要想多,开释里面的rpm包而已)$ sh jdk-6u38-linux-x64-rpm.bin# 查看下得到的rpm包$ ll *.rpm# 安装Oracle JDK$ sudo rpm -ivh jdk-6u38-linux-amd64.rpm# OK 至此已完成Oracle JDK安装# 查找安装的Oracle JDK目次# 查找jdk安装名称$ rpm -qa | grep ^jdk-1.6.0jdk-1.6.0_38-fcs.x86_64# 根据安装名称查找安装到本地的文件列表$ rpm -ql jdk-1.6.0_38-fcs.x86_64.../usr/java/jdk1.6.0_38 # Oracle JDK HOME...# 以上查找到的目次后面会用到
复制代码
  1. 错误:gcc: error: unrecognized command line option '-mimpure-text'办理:vi jdk/make/common/shared/Compiler-gcc.gmk在70行remove the command "-mimpure-text" in the code:
复制代码
  1. 错误:Error: time is more than 10 years from present: 1136059200000办理:# 修改以下文件,将日期改为十年以内,JDK的Bug。vi jdk/src/share/classes/java/util/CurrencyData.properties# line: 108  377  439  529  555
复制代码
  1. 错误:../../../src/share/classes/sun/management/jmxremote/ConnectorBootstrap.java:661: error: no suitable constructor found for SslRMIServerSocketFactory(SSLContext,String[],String[],boolean)办理:vi ./jdk/src/share/classes/sun/management/jmxremote/ConnectorBootstrap.java注释掉662行的参数
复制代码
资料


  • 《深入明白Java虚拟机
  • openjdk7之编译和debug | YDDMAX https://yddmax.github.io/2017/06/11/openjdk7%E4%B9%8B%E7%BC%96%E8%AF%91%E5%92%8Cdebug/
  • centos7编译openjdk7常见问题 - clover灬 - OSCHINA https://my.oschina.net/zhangdq/blog/2250314
  • How to build and package OpenJDK 7 on Linux · hgomez/obuildfactory Wiki https://github.com/hgomez/obuildfactory/wiki/How-to-build-and-package-OpenJDK-7-on-Linux
  • ubuntu16.04编译JDK7 - 简书 https://www.jianshu.com/p/32dc1a850e23
  • Building OpenJDK7 with CentOS7 – Rtfsc8 http://blog.rtfsc8.top/2018/07/07/building-openjdk7-with-centos7/
  • CentOS上编译OpenJDK8源码 以及 在eclipse上调试HotSpot虚拟机源码 - tjiyu的博客 - CSDN博客
本文独立博客地址:JVM源码分析-JVM源码编译与调试 | 木杉的博客

相关技术服务需求,请联系管理员和客服QQ:2753533861或QQ:619920289
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

帖子推荐:
客服咨询

QQ:2753533861

服务时间 9:00-22:00

快速回复 返回顶部 返回列表