zoukankan      html  css  js  c++  java
  • HotSpot学习(一):编译、启动与调试

    前文

    JVM对许多Java程序员是一个黑盒子,经常需要与它打交道,但是又搞不清内部的原理。
    我出于以下几个目的决定对JVM内部做一些学习:

    • 之前对虚拟机的了解停留在理论层面上,通过学习,做到知其然,知其所以然
    • 工作中可能涉及JNI的一些调试,JNI接口的C++端离不开JVM相关的结构和函数
    • 在了解虚拟机后,帮助改善程序性能

    相关环境说明

    以下是我的环境说明:

    • 操作系统:Windows上通过VMStation装了Ubuntu 18.04的虚拟机
    • 编译的版本:直接从github上搜的openjdk的项目,从tag中找了openjdk8的源码包下载的
    • 引导的JDK版本: 编译JDK时需要用另一个JDK做引导(自举?),我这里用的jdk.1.7.0_76
    • gcc的版本:编译时会由于gcc的版本的过高报错,我重新换了gcc v4.8.5

    基本的编译环境就是以上这些。

    编译过程

    我之前参考的是《深入理解Java虚拟机》和《HotSpot实战》两本书进行编译的。走了不少弯路,主要原因是书中介绍的编译方式相对复杂,而且是针对JDK7甚至更早之前的版本。 现在已经不具有太多的参考价值。
    openjdk8开始采用了相对简单的编译方式:

     configure & make
    

    也就是编译方式共分为两步:配置和编译。

    配置

    配置主要是通过configure命令生成Makefile。在openjdk的主目录下输入以下命令:

    bash ./configure --with-target-bits=64 --with-debug-level=slowdebug --with-boot-jdk=/your boot jdk path/jdk1.7.0_80/
    

    以上几个参数分本指定了编译的版本、编译的等级和引导JDK的路径(需要改完你自己的boot jdk所在的路径)

    编译

    配置完后,就可以用make命令开始编译了:

    make all DEBUG_BINARIES=true
    

    我直接选了make all,会对所有部分都进行编译。

    之后大概等十多分钟,如果看到输出类似的内容说明编译成功(这里的时间较短,是因为我之前已经编译过):

    ---- Build times -------
    Start 2020-12-19 11:27:05
    End   2020-12-19 11:28:33
    00:00:00 corba
    00:00:00 demos
    00:01:15 docs
    00:00:00 hotspot
    00:00:08 images
    00:00:00 jaxp
    00:00:00 jaxws
    00:00:03 jdk
    00:00:01 langtools
    00:00:00 nashorn
    00:01:28 TOTAL
    -------------------------
    Finished building OpenJDK for target 'all'
    

    编译完成后,我们可以看到之前存JDK源码的地方多了build的文件夹,下面有一个linux-x86_64-normal-server-slowdebug的文件夹,这就是我们编译生成的文件,其中的内容如下:

    xieshang@xieshang-virtual-machine:~/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug$ ls
    bootcycle-spec.gmk  config.status        hotspot-spec.gmk  Makefile
    build.log           configure-arguments  images            nashorn
    build.log.old       corba                jaxp              source_tips
    compare.sh          docs                 jaxws             spec.gmk
    config.h            docstemp             jdk               spec.sh
    config.log          hotspot              langtools         tmp
    
    

    验证编译是否正确的简单方法可以执行java -version命令,看程序是否可以正常输出。
    进入上面路径下的./jdk/bin目录下,然后运行./java -version,观察输出,如果正常输版本,则说明编译正确。

    xieshang@xieshang-virtual-machine:~/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/bin$ ./java -version
    openjdk version "1.8.0-internal-debug"
    OpenJDK Runtime Environment (build 1.8.0-internal-debug-xieshang_2020_12_18_09_49-b00)
    OpenJDK 64-Bit Server VM (build 25.40-b25-debug, mixed mode)
    

    我在编译过程中碰到的问题

    缺少依赖

    编译过程中如果缺少依赖,可以根据提示,通过apt-get install命令安装

    gcc版本过高

    这个在上文中提到过了,可以在装一个低版本的gcc。可以参考这里

    系统版本不对

    如果出现类似如下的错误

    recipe for target 'check_os_version' failed
    

    说明是操作系统内核版本过高,编译时校验不通过,解决的办法是修改./hotspot/make/linux/Makefile, 文件中搜索SUPPORTED_OS_VERSION

    修改228行内容: SUPPORTED_OS_VERSION = 2.4% 2.5% 2.6% 2.7% 为 
    SUPPORTED_OS_VERSION = 2.4% 2.5% 2.6% 2.7% 3% 4%
    

    说一个罕见的坑

    由于我在系统环境变量中打开了_Java_LAUNCHER_DEBUG标志,导致在gensrc时,生成Java代码中包含了很多debug的打印,破坏了Java类的结构,导致编译失败。
    这种情况通常也不会出现,如果出现只需要在环境变量中删除这个标志即可。
    报错的内容如下:

    Generating Nimbus source files
    [Error] encoded value was less than 0: encode(-8.326673E-17, 5.0, 11.0, 16.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
    [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
    [Error] Encountered Infinity: encode(-0.00877193, 0.0, 7.0, 7.0)
    /home/xieshang/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/gensrc/java/nio/ByteBuffer.java:1583: 错误: 非法的类型开始
    ----_JAVA_LAUNCHER_DEBUG----
    ^
    /home/xieshang/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/gensrc/java/nio/ByteBuffer.java:1583: 错误: 需要';'
    ----_JAVA_LAUNCHER_DEBUG----
      ^
    /home/xieshang/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/gensrc/java/nio/ByteBuffer.java:1583: 错误: 需要<标识符>
    ----_JAVA_LAUNCHER_DEBUG----
                            ^
    /home/xieshang/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/gensrc/java/nio/ByteBuffer.java:1583: 错误: 非法的类型开始
    ----_JAVA_LAUNCHER_DEBUG----
                              ^
    /home/xieshang/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/gensrc/java/nio/ByteBuffer.java:1584: 错误: 需要';'
    Launcher state:
            ^
    /home/xieshang/learn-jvm/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/gensrc/java/nio/ByteBuffer.java:1584: 错误: 需要<标识符>
    Launcher state:[1217/142857.833:ERROR:directory_reader_win.cc(43)] 
    ...后续省略
    

    调试

    调试的过程中,我也踩了不少坑。
    选择的IDE从最初的NetBeans到VSCode再到CLion,可以说都试了个遍。
    除了NetBeans没成功,后两个都能正常调试了。
    Java程序员可能对IDEA比较熟悉,因此我在这里介绍使用CLion的调试步骤。
    我用的版本是2020.1的,之后的版本界面可能有些许不同,但也大差不差。

    第一步:导入项目
    step1

    第二步:选中openjdk的根目录
    step2

    第三步:选择Import as a new CMake project
    step3

    第四步:在弹出的对话框中,一定要把编译结果中的jdk/bin路径选上
    step4

    项目导入后,CLion会现对整个项目进行一次索引,过程可能会持续几分钟,待索引结束后,我们便可以开始配置调试的环境。

    第五步:配置调试环境
    step5

    完成以上步骤后,就可以运行程序开始调试。
    output

    调试中遇到的问题

    我在调试过程中还是比较顺利的,唯一遇到的问题就是断电无法进入jni.cpp中,如果强制进入的话 会跳入汇编的界面。
    这个可以将找一下libjvm.diz中的libjvm.debuginfo解压出来(注意:压缩包的位置需要找对)
    unzip

    另一个问题是在调试时,可能会有SIGSEGV信号出现,这个不会影响调试过程,如果你不喜欢,可以用GDB的handle nostop命令关闭该信号。
    gdb-nostop

  • 相关阅读:
    【转】Java中super和this的几种用法与区别
    公寓上网新认证方式破解研究
    移动设计
    破解哈工程大学深澜认证路由器
    Arch Linux安装记录
    foreach新解
    工具大全(转载)
    Linux 入门
    设置为驼峰命名
    efcore Add-Migration 错误
  • 原文地址:https://www.cnblogs.com/insaneXs/p/14167293.html
Copyright © 2011-2022 走看看