`
lemoncyb
  • 浏览: 51110 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

[转]在Linux下编译OpenJdk并调试Hotspot

 
阅读更多
背景

对于大多数Java程序员来说,JVM就是一个黑盒子,我们一般不必关心它内部是怎么运作的。但是万一碰到JVM bug导致的Crash呢,又或者只是因为好奇想了解JVM的内部世界,那么就需要编译和调试JVM。
概念

1. JVM(Java Virtual Machine)

       翻译过来就是Java虚拟机,所谓虚拟机是相对传统的计算机而言的。

       传统的计算机有很多体系架构,比如x86,Sparc等等;一个体系架构一般有类似的指令集,比如586兼容所有386的指令集,但是又有自己扩充的指令。在Java出现之前,大部分编译型语言(我不知道是不是所有的)都会由编译器把源代码编译成特定机器下特定指令集的机器指令。从理论上来说所有的机器的表达能力都是一样的,等价于图灵机这个理论模型。但是就具体实现而言,在不同机器实现同一个逻辑的机器指令是不同的,这就导致在一台机器编译的二进制文件可能无法在另一台机器上运行(这里不考虑操作系统的差异)。当然写得比较好的c代码是可以在不同平台编译,但是一般不太可能把一个windows下的exe程序让linux运行。(当然除了机器指令的差别,不同系统的二进制格式也是不能兼容的,比如windows pe和linux elf)。即使同一个操作系统,比如linux,在x86下编译的程序也是没办法在Sparc下跑的,因为Sparc根本不认识x86的指令。

       JVM就是一个虚拟机,它定义了自己的指令集(Java字节码),所有的Java程序编译成统一的字节码,然后JVM负责在不同的硬件和操作系统下解释或者编译字节码。

       JVM本身只是一个概念,一种抽象

2. JRE(Java Runtime Environment)

       Java运行时环境,首先必须实现JVM规范,实现同样的JVM规范可以有很多方法,比如Sun有Sun的实现,IBM有IBM的实现。

       JVM当然得实现Java语言,也就是java.lang.*,认识int,知道class,方法,...

       但是如果你发现没有java.util.* 或者没有java.io.*时,是不是会发现没法写Java程序了呢?

       没有util,你当然可以自己实现那些常见的数据结构,这可以用java实现;那没有io呢?那似乎没法写出跨平台的代码了。

       所以除了实现语言本身,Java还定义了很多跨平台的API,比如java.awt java.io等等。这些可能是我们非常常用的,所以Sun把它们叫作了Java Standard Edition(Java SE)。一般以java开头

       另外有些API,比如JDBC,这是连接数据库的API。这些组成了Java Enterprise Edition(Java EE)。一般以javax包开头

       这些API有些是用Java实现,但很多不跨平台的必须由C实现(JVM通过JNI规范来实现Java和c的数据传递),所以JRE必须实现这些

3. JDK(Java Development Kit)

*       *开发者的工具,最常用的就是javac编译器。和vc或者gcc一样,必须要有工具把源代码编译成目标代码,c的目标代码是机器相关的,而Java的目标代码就是字节码。

*       *当然实现javac就是实现一个传统的编译器,词法分析,语法分析和语义分析,然后翻译成字节码。这本身不是件简单的事情,所以编译器的开发者也开发了一些帮助开发编译器的工具,比如我们熟悉的lex/flex,yacc/bison。java也实现了类似的JavaCC和JFlex。

       除了javac,javadoc和javah也是很常用的工具。此外Java的新特性,比如范型(Generics)和注解(Annotations),这些也需要由编译器来处理。(范型只影响编译,编译后被擦除了,但是Annotation可能会运行时能通过反射获取)。这些代码一般都是Java实现,打包到tools.jar。所以如果你需要动态编译Java代码(比如JSP),那么你可能要用到tools.jar
Linux下Build Open Jdk 7和Debug Hotspot

      下面的内容大部分参考下面两篇文章:

        http://weblogs.java.net/blog/simonis/archive/2008/01/hotspot_develop.html

        http://weblogs.java.net/blog/simonis/archive/2008/01/hotspot_develop_1.html

       不过在用NetBeans调试Hotspot时碰到了一些问题(可能是我用了比较新的netbeans的缘故),之前问过作者,不过没有得到满意的解答,后来自己使用了一些trick的手段。

1. 获取源代码

       两种方法

       1.1 使用 Mercurial 获取最新代码(其实就是svn git这样的scm工具)

             http://hg.openjdk.java.net/jdk7/build/raw-file/tip/README-builds.html

             速度比较慢,可能要花几个小时吧,如果你需要最新代码又不想等,可以找我

       1.2 直接下载压缩的源代码包

             http://download.java.net/openjdk/jdk7/

             解压后就可以了,如果想更新代码,也可以运行 sh ./getSource.sh更新

        1.3 源代码结构

             hotspot

                  hotspot的源代码,完整的工程,包括makefile,文件按平台分类,比如 hotspot/src下有4个目录:os cpu os_cpu share

                  很明显,share放的是于平台无关的代码,os里放的是特定os的代码,cpu放的是特定cpu的代码,os_cpu放的是特定os和cpu的代码

             jdk

                  java api的代码,如果我们对jdk的代码感兴趣,比如想看ConcurrentHashMap的实现,那么可以去src/share/classes/java/util/concurrent/ConcurrentHashMap.java

                  它的目录结果是 share,linux,solaris和windows,和前面类似,share放的是操作系统无关的代码,一般是用Java实现。Windows当然放的是windows的实现,不过*nix的都是放到solaris下实现的,不同的操作系统通过不同的#define区别,所以大部分代码都在solaris下,linux目录下只有一些文档。大部分代码都是c实现,然后通过JNI让java调用,所以大部分代码都在native下而不是classes下。
             langtool
                  编译器工具,比如javac,javah,apt等等工具
             make
                  makefile
             其它
                  比如jaxp,corba等等,暂时不感兴趣
./share/classes/java/util/concurrent/ConcurrentHashMap.java

2. 编译和调试

         大部分应该都装好了,比如gcc,make,ant,bootstrap jdk,还有一些需要安装,比如cups等Sun没有版权的代码,你必须自己从网上下载(或者通过Linux发行版的仓库安装)

         由于我的机器很早以前编译的,当时编译时少一个装一个,所以忘了要装哪些了,碰到问题的可以google,也可以找我讨论(我也可能解决不了,我对Linux也不是很熟悉)

         可以使用 make sanity 测试,请设置如下的环境变量(我的是bash):      

export LANG=C
unset JAVA_HOME
export ALT_BOOTDIR=/home/lili/java/jdk1.6.0_26/
export ALLOW_DOWNLOADS=true
export USE_PRECOMPILED_HEADER=true
export SKIP_DEBUG_BUILD=false
export SKIP_FASTDEBUG_BUILD=true
export DEBUG_NAME=debug

  2.1 编译JDK

         如果make sanity通过,那么直接运行 make,如果你想保留日志,那么可以 make 2>&1 |tee ~/buildopenjdk7.log

         可能会碰到问题,需要你解决。如果顺利的话,几个小时后应该能编译好。

   2.2 编译结果

          费了这么大的力气,我们看看编译的成果。

          编译的结果放到了build/linux-i586-debug下,

              bin

                  编译后的二进制文件,比如java,javac等等

              lib

                  jar包,比如tools.jar

               hotspot

                  我们最感兴趣的jvm,具体运行和调试会在下面介绍

               classes

                  java api部分,包括按包组织的java类

   2.3  运行一下自己编译的jdk              

lili@lili-desktop:/media/d/openjdk7zip/openjdk/build/linux-i586-debug/j2sdk-image$ ./bin/java -version
openjdk version "1.7.0-internal-debug"
OpenJDK Runtime Environment (build 1.7.0-internal-debug-lili_2011_11_14_12_30-b00)
OpenJDK Server VM (build 21.0-b17-jvmg, mixed mode)
lili@lili-desktop:/media/d/openjdk7zip/openjdk/build/linux-i586-debug/j2sdk-image$

   2.4  编译hotspot     

        前面说过了,我们感兴趣的是hotspot。如果想学习util里的实现,直接能看java代码,如果想学习io或者nio,那么可以直接看native c的实现(java通过JNI调用)。

        比如我们修改了hotspot的代码,当然可以重新编译整个JDK,但这太慢,我们可以只重新编译hotspot自己

        环境变量和前面一样,需要ALT_BOOTDIR和ALT_OUTPUTDIR

        使用:cd hotspot/make && make jvmg jvmg1      

         target jvmg会编译Server版本的Hotspot,并且是带调试符号的,jvmg1会编译Client版本的,同样也是带调试符号的。因为优化可能会调整代码顺序或者去掉一些无用的代码等,这会给调试带来困难,所以如果我们是为了学习的话,用这两个target就好了。

         如果需要优化的版本可以用target optimized或者optimized1,同样后面有1的表示Client;产品的target是product和product1

  2.5 运行hotspot

*         *需要增加两个环境变量LD_LIBRARY_PATH和JAVA_HOME,前者参考下面,里面包含我们编译好的libjvm.so,后者可以用我们自己编译的jdk

export LD_LIBRARY_PATH=/media/d/openjdk7zip/openjdk/build/hotspot_debug/linux_i486_compiler1/jvmg
export JAVA_HOME=/media/d/openjdk7zip/openjdk/build/linux-i586-debug/j2sdk-image
cd /media/d/openjdk7zip/openjdk/build/hotspot_debug/linux_i486_compiler1/jvmg
lili@lili-desktop:/media/d/openjdk7zip/openjdk/build/hotspot_debug/linux_i486_compiler1/jvmg$ ./gamma -versionlili@lili-desktop:/media/d/openjdk7zip/openjdk/build/hotspot_debug/linux_i486_compiler1/jvmg$ ./gamma -version
Using java runtime at: /media/d/openjdk7zip/openjdk/build/linux-i586-debug/j2sdk-image/jre
openjdk version "1.7.0-internal-debug"
OpenJDK Runtime Environment (build 1.7.0-internal-debug-lili_2011_11_14_12_30-b00)
OpenJDK Client VM (build 21.0-b17-internal-jvmg, mixed mode)Using java runtime at: /media/d/openjdk7zip/openjdk/build/linux-i586-debug/j2sdk-image/jreopenjdk version "1.7.0-internal-debug"
OpenJDK Runtime Environment (build 1.7.0-internal-debug-lili_2011_11_14_12_30-b00)
OpenJDK Client VM (build 21.0-b17-internal-jvmg, mixed mode

2.6 使用gdb调试hotspot

         前面我们编译出来的hotspot已经是带有调试符号,所以可以使用gdb来调试了。

gdb ./gamma


GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
........
(gdb) break main
(gdb) r
Starting program: /media/d/openjdk7zip/openjdk/build/hotspot_debug/linux_i486_compiler1/jvmg/gamma
[Thread debugging using libthread_db enabled]


Breakpoint 1, main (argc=1, argv=0xbffff1a4)
    at /media/d/openjdk7zip/openjdk/hotspot/src/share/tools/launcher/java.c:228
228 {
(gdb) (gdb)       

    可以看到,gamma测试程序的入口是hotspot/src/share/tools/launcher/java.c。

  2.7 使用Netbeans调试hotspot 

      对于习惯GNU/Linux环境开发的c程序员来说,有源代码和Makefile,再配合vim或者emacs,使用它们的插件比如cscope来阅读代码,gdb来调试程序,是件很自然的事情。

      不过对于习惯的IDE的Java程序员来说,这确实有的不爽。估计Sun的人也是这样的,所以他们内部是使用Netbeans来调试hotspot的。

      HotSpot development on Linux with NetBeans - Part 2 这篇blog是基于Netbeans6,里面很多都是为了解决nb的bug,我使用的是Netbeans7,所以很多bug已经修正。

       2.7.1 NB项目设置

          新建项目->C/C+->基于现有源代码的C/C+项目

          指定包含源代码的目录:/media/d/openjdk7zip/openjdk/hotspot,由于makefile不在这个目录下,而是在它的子目录下,所以选择“定制”模式。

          然后选择好Makefile的位置。下一步进入build的配置

          make的工作目录是make,

          生成命令是${MAKE} -f Makefile jvmg,         
         清理命令是${MAKE} -f Makefile clean,

          生成结果填写一个目录就可以了。

          然后点击确定就会buildhotspot,不过这样会出现问题,因为缺少ALT_BOOTDIR环境变量,但是Netbeans好像只能设置运行时的环境变量,而不能设置build时的环境变量,参考

这篇文章 http://forums.netbeans.org/post-40251.html。我折腾了好久也没有搞定,最后也没搞定。最后只能使了个毛招:        

因为它默认去/java/re/j2se/1.6.0/latest/binaries/linux-i586找jdk,所以可以建个符号链接

lili@lili-desktop:/java/re/j2se/1.6.0/latest/binaries$ ll linux-i586
lrwxrwxrwx 1 lili lili 28 Nov 14 18:14 linux-i586 -> /home/lili/java/jdk1.6.0_26/

           这下可以点击确定,让它build。

       2.7.2 删除“无用”的文件

           因为hotspot是跨平台的项目,前面可以看到针对不同的平台会有不同的实现,但是很多文件是相同的,比如文件名相同,因此同一个方法可能在linux里有个实现,windows有个实现,

           这样NB就不知道到底会是哪个了,这会对Code Assistance带来影响,另外我们查看定义也会带来问题。所以针对Linux平台,我们可以在NB里删除不需要的文件(这不是删除磁盘上的文件,而是在NB里删除)

           可以删除的目录:              

   hotspot/agent/src/os/solaris
hotspot/agent/src/os/win32
hotspot/agent/src/share/native/jvmdi
hotspot/src/cpu/sparc
hotspot/src/os/solaris
hotspot/src/os/windows
hotspot/src/os_cpu/solaris_sparc
hotspot/src/os_cpu/solaris_x86
hotspot/src/os_cpu/windows_x86
hotspot/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp
hotspot/src/share/vm/utilities/globalDefinitions_visCPP.hpp

          可以删除的文件(我的机器是x86,所以把x86-64的那些文件可以删掉):

    hotspot/src/cpu/x86/vm/assembler_x86_64.cpp
hotspot/src/cpu/x86/vm/assembler_x86_64.hpp
hotspot/src/cpu/x86/vm/assembler_x86_64.inline.hpp
hotspot/src/cpu/x86/vm/dump_x86_64.cpp
hotspot/src/cpu/x86/vm/interp_masm_x86_64.cpp
hotspot/src/cpu/x86/vm/interp_masm_x86_64.hpp
hotspot/src/cpu/x86/vm/interpreterRT_x86_64.cpp
hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp
hotspot/src/cpu/x86/vm/jniFastGetField_x86_64.cpp
hotspot/src/cpu/x86/vm/runtime_x86_64.cpp
hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp
hotspot/src/cpu/x86/vm/stubGenerator_x86_64.cpp
hotspot/src/cpu/x86/vm/stubRoutines_x86_64.cpp
hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp
hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp
hotspot/src/cpu/x86/vm/templateTable_x86_64.hpp
hotspot/src/cpu/x86/vm/vm_version_x86_64.cpp
hotspot/src/cpu/x86/vm/vm_version_x86_64.hpp
hotspot/src/cpu/x86/vm/vtableStubs_x86_64.cpp
hotspot/src/cpu/x86/vm/x86_64.ad
hotspot/src/os_cpu/linux_x86/vm/assembler_linux_x86_64.cpp
hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.ad
hotspot/src/os_cpu/linux_x86/vm/linux_x86_64.s

     2.7.3 在NB里调试

           修改项目属性->运行

              运行命令:"{OUTPUT_PATH}" -XX:StopInterpreterAt=1 -version (如果想调试某个Java类,那么配好-classpath和类名)

              运行目录:make

              环境变量:配置好JAVA_HOME和LD_LIBRARY_PATH

           在java.c的main里头加个断点

           右键点击项目->调试

      have fun ~
分享到:
评论

相关推荐

    OpenJDK8U-jdk_x64_linux_hotspot_8u265b01.tar.gz

    在Linux环境下,OpenJDK 8是开发和运行Java应用程序的重要组件。它包含JRE(Java运行环境)和JDK(Java开发工具包),JDK除了JRE之外,还包含了用于编译、调试和分析Java程序的工具,如javac(Java编译器)、javadoc...

    OpenJDK8U-jdk_x64_linux_hotspot_8u275b01.tar.gz

    《OpenJDK8U-jdk_x64_linux_hotspot_8u275b01:深入了解Java开发的关键工具》 OpenJDK8U-jdk_x64_linux_hotspot_8u275b01是一款针对64位Linux系统的OpenJDK 8更新版本,该版本号为8u275b01。OpenJDK,全称Open ...

    OpenJDK11U-jdk_x64_linux_hotspot_11.0.6_10.tar.gz

    总的来说,OpenJDK11U-jdk_x64_linux_hotspot_11.0.6_10.tar.gz 提供了一个在Linux系统上运行Java应用所需的环境,特别是对于那些基于CentOS 7的服务器。由于国内下载速度较慢,用户可能需要寻找镜像站点或者使用...

    OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz

    OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz 是一个针对64位Linux操作系统的OpenJDK 8更新版本(Update 232)的压缩包,其中包含了Java Development Kit(JDK)。OpenJDK是一个开源的Java平台实现,它遵循GNU ...

    OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz

    在本例中,我们关注的是11.0.11版本的HotSpot JVM实现,适用于64位的Linux操作系统。 标题中的"OpenJDK11U-jdk_x64_linux_hotspot_11.0.11_9.tar.gz"揭示了几个关键信息: 1. **OpenJDK**: 开源的Java Development...

    openjdk-18.0.1.1(openjdk-18.0.1.1_linux-aarch64_bin.tar.gz)

    有了 OpenJDK,开发者可以在 aarch64 Linux 上进行 Java 应用的开发、编译、测试和部署。`javac` 命令用于编译 Java 源代码,`jar` 工具用于创建和管理 JAR 文件,`javadoc` 用于生成文档,而 `jdb` 是 Java 的调试...

    openjdk 21 下载 ,开源版本

    在下载并安装OpenJDK 21时,需要注意以下几点: 1. **系统兼容性**:确保你的操作系统支持OpenJDK 21,因为不同版本可能对Windows、macOS或各种Linux发行版有不同的支持。 2. **环境变量配置**:安装后,你需要将...

    linux版本的openjdk7,有需要的可以下载

    在OpenJDK 7中,JVM采用了HotSpot技术,它结合了编译器和解释器的优点,能够在运行时优化代码性能。 2. **Java类库**:OpenJDK 7提供了大量的Java API,包括基础类库如集合框架、I/O流、网络编程、多线程等,以及...

    OpenJDK8U-jdk-aarch64-linux-hotspot-8u372b07.tar.gz

    OpenJDK8U-jdk-aarch64-linux-hotspot-8u372b07.tar.gz 是一个针对arm架构的Linux系统优化的Java Development Kit(JDK)版本,主要适用于在基于ARM处理器的设备上进行Java应用程序和库的开发。这个版本是OpenJDK 8...

    openjdk8-linux

    在OpenJDK 8中,JVM采用了HotSpot技术,它包含即时编译器(JIT),能将频繁执行的字节码转换为优化的本地机器代码,从而提升性能。 2. **类库**:OpenJDK 8提供了丰富的标准类库,包括集合框架、I/O流、网络编程、...

    openjdk-17.0.10+7-with-openjfx

    JavaFX支持跨平台运行,这意味着使用OpenJFX开发的应用程序可以在Windows、macOS和Linux等操作系统上无缝运行。此外,JavaFX还支持多种硬件加速技术,如OpenGL和DirectX,以提供流畅的用户体验。 在实际开发中,...

    openjdk7-master.zip

    通过解压"openjdk7-master.zip",你可以自行编译OpenJDK7,了解其内部机制,并进行自定义优化或扩展。这对于Java开发者来说,是提高技能、深入学习Java底层原理的绝佳途径。你可以学习到如何配置和构建OpenJDK,如何...

    openjdk7u源码

    4. **垃圾收集器**:OpenJDK的垃圾收集算法如Parallel GC、Concurrent Mark Sweep (CMS) 和G1都包含在"src/hotspot/share/gc"目录下,它们的实现细节对于理解和优化Java应用内存性能至关重要。 5. **并发与多线程**...

    基于编译虚拟机jvm—openjdk的编译详解

    本文将详述如何在Linux环境下编译OpenJDK,帮助开发者从源码层面探索Java虚拟机的内部机制。 首先,了解OpenJDK的重要性。Java的跨平台特性主要得益于JVM,它负责解析、执行Java字节码,提供运行环境。虽然通常...

    openjdk-7-fcs-src-b147-27_jun_2011.zip

    - **构建系统**: 探索Makefile和Ant脚本,了解如何在Windows环境中编译OpenJDK源码。 - **调试工具**: 如JDB、VisualVM等,帮助我们调试和分析OpenJDK的运行情况。 7. **性能优化** - **性能分析**: 使用JFR...

    Linux下安装Eclipse/JRE/CDT开发C/C++

    总结:在Linux环境下,Eclipse配合CDT为C/C++开发者提供了一个强大的开发工具链,它简化了开发流程,增强了代码编辑和调试体验,同时也方便了项目管理和团队协作。通过安装和配置JRE、Eclipse及CDT,开发者可以在...

    linux arm 版本的jdk

    Linux ARM版本的JDK是Java开发工具包在基于ARM架构的Linux操作系统上的特定构建。ARM架构,全称为Advanced RISC Machines,是一种广泛应用于嵌入式系统、移动设备以及某些服务器平台的精简指令集计算机(RISC)架构...

    Openjdk7master.zip

    在 Ubuntu 系统上,你可以使用 `make` 命令来编译 OpenJDK 7 源码,这将帮助你了解构建过程和依赖关系。同时,配合调试工具(如 gdb)和 IDE(如 Eclipse 或 IntelliJ IDEA),可以更深入地探索源码。 总之,...

    jdk下载/Linux64位 JDK8最新版本!!!

    Java Development Kit(JDK)是Java编程语言的核心组件,它为开发者提供了编译、调试和运行Java应用程序所需的所有工具。本文将详细介绍Oracle JDK 1.8在Linux 64位系统上的安装和使用,以及其对Java开发的重要性。 ...

    OpenJDK Java开发环境.zip

    OpenJDK,全称为Open Source JDK,是Java Development Kit(JDK)的一种开源实现,它...通过理解和掌握这些知识点,开发者可以更好地利用OpenJDK进行Java应用程序的开发和调试,从而提高生产力并确保代码的质量和性能。

Global site tag (gtag.js) - Google Analytics