内容简介:要分析JVM的源码,结合资料直接阅读是一种方式,但是遇到一些想不通的场景,必须要结合调试,查看执行路径以及参数具体的值,才能搞得明白。所以我们先来把JVM的源码进行编译,并能够使用GDB进行调试。本文使用的JDK版本:OpenJDK7,分支b147下载页面:
要分析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
安装依赖
yum -y install gcc gcc-c++ make yum -y install alsa-lib-devel yum -y install cups-devel yum -y install libX* yum -y install gcc gcc-c++ yum -y install libstdc++-static wget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo yum -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
$ sh jdk-6u38-linux-x64-rpm.bin $ sudo rpm -ivh jdk-6u38-linux-amd64.rpm
编写编译脚本
解压openjdk:
unzip openjdk-7-fcs-src-b147-27_jun_2011.zip
在openjdk目录下添加一个build.sh脚本:
#!/bin/bash export LANG=C #将一下两项设置为你的BootstrapJDK安装目录 export ALT_BOOTDIR=/usr/java/jdk1.6.0_38 export ALT_JDK_IMPORT_PATH=/usr/java/jdk1.6.0_38 #允许自动下载依赖包 export ALLOW_DOWNLOADS=true #使用预编译头文件,以提升便以速度 export USE_PRECOMPILED_HEADER=true #要编译的内容,我只选择了LANGTOOLS、HOTSPOT以及JDK export BUILD_LANGTOOLS=true export BUILD_JAXP=false export BUILD_JAXWS=false export BUILD_CORBA=false export BUILD_HOSTPOT=true export BUILD_JDK=true #要编译的版本 export SKIP_DEBUG_BUILD=false export SKIP_FASTDEBUG_BUILD=true export DEBUG_NAME=debug #避免javaws和浏览器 Java 插件等的build BUILD_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/output unset CLASSPATH unset JAVA_HOME make sanity DEBUG_BINARIES=true make 2>&1
然后执行 sh build.sh 进行编译。如果编译过程中遇到问题,可以查阅下文的编译问题解决的部分。
运行HotSpot
编译成功后,编译的输出默认在 openjdk/build
目录下。HotSpot的编译输出在 openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg
目录下。
使用HotSpot提供的命令执行测试:
cd build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg # test_gamma是HotSpot提供的一个测试程序,可以成功执行说明编译成功 ./test_gamma # 使用hotspot脚本进行GDB调试 ./hotspot -gdb HelloWorld
./hotspot
是一个脚本,查阅代码可以看出他做了一些简单的事情,主要是会设置环境变量:
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参数脚本,并运行GDB命令:
gdb -x /tmp/hsl.26037
/tmp/hsl.26037是脚本生成的gdb参数,我们可以看看都设置了什么:
cd /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg handle SIGUSR1 nostop noprint handle SIGUSR2 nostop noprint set args HelloWorld file /root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/gamma directory /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.so break InitializeJVM run # Stop in InitializeJVM delete 1 # We can now set breakpoints wherever we like
可以看出设置了源码目录,设置了一个默认断点。分析了hotspot脚本后,我们可以根据需要用最原始的方式来启动hotspot和gdb来实现更复杂的调试需求,比如远程调试。
直接执行的方式:
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调试:
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远程调试:
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对应字符串:
p *name._body@name._length
如何打印KlassHandle对应的类名?
p Klass::cast(current_klass.obj())->external_name()
添加加载特定类时的断点
break ClassFileParser::parseClassFile if strncmp(class_name._body, "XXX", 3) == 0 break SystemDictionary::load_instance_class if strncmp(class_name._body, "XXX", 3) == 0
遇到的问题
BootstrapJDK一开始设置为JDK8会失败,要改为JDK6
#错误 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
#错误 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
#错误 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
#错误 /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
#错误 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.0 jdk-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 ... # 以上查找到的目录后面会用到
错误: 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:
错误: 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
错误: ../../../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博客
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Golang源码探索(一) 编译和调试源码
- 写Java这么久,JDK源码编译过没?编译JDK源码踩坑纪实 原 荐
- 使用源码编译Hadoop
- Runtime源码编译
- 下载编译AOSP源码
- 源码编译LNMP平台架构
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Data Structures and Algorithm Analysis in Java
Mark A. Weiss / Pearson / 2011-11-18 / GBP 129.99
Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course wa......一起来看看 《Data Structures and Algorithm Analysis in Java》 这本书的介绍吧!