当前位置:   article > 正文

32位jdk_JVM 一窥:从手动编译 JDK 开始

openjdk jvm二次开发

e1775a991f4904d9dd636262211fc3bf.png

JDK,全名 Java Development Kit ,包含了从事 Java 程序开发所需的基本工具。我们在各大平台中所使用的 JDK ,都是由 Oracle 提供的通用版本。由于仅需要简单地环境变量,就可以在上层进行 Java 编程,因此笔者层忽略了 JDK 内部的诸多细节。

如果我们可以弄清楚 JDK 具体包含了什么,那么在日后我们甚至可以通过源码对 JDK 本身进行一些微小的调整。笔者最近入手了周志明所著的《深入理解 Java 虚拟机》,并计划沿着该书的大纲内容对 JVM 虚拟机做深入了解。获取书籍《深入理解 Java 虚拟机》

在一窥 JVM 运行机理之前,笔者选择先从手动编译 JDK 开始。建议在开始编译之前先阅读一下 Open Jdk 官方文档:http://hg.openjdk.java.net/jdk8u/jdk8u/raw-file/tip/README-builds.html

OpenJDK 目录结构

下面是 OpenJDK 的目录结构,从中我们可以一窥到 JDK 所包含的组件,包括 Java 的核心内容 —— 虚拟机。

  1. openjdk
  2. —— corba:多语言、分布式通讯接口
  3. —— hotspot:Java 虚拟机
  4. —— jaxp:XML 处理
  5. —— jaxws:一组 XML web services 的 Java API
  6. —— jdk:java 开发工具包
  7. —— langtools:Java 语言工具
  8. —— nashorn:JVM 上的 JavaScript 运行时

OpenJDK 与 Oracle JDK

我们在这里将使用 OpenJDK 用于编译练习,而之前,我们都是从 Oracle 官网中直接下载并使用 Oracle JDK。 Java 的发展历程背后是诸多科技公司之间的“宫斗史”,笔者在这里不会再提及。

这里仅简单叙述 OpenJDK 和 Oracle JDK 之间的联系: OpenJDK 是 Sun 公司在 2006 年将 Java 开源的项目。各大科技公司都具有基于 OpenJDK 进行二次开发后的独立 JDK,包括 IBM 公司的 J9,也包括 Oracle 公司的 Oracle JDK。

OpenJDK 和 Oracle JDK 从使用上几乎感觉不出任何区别,毕竟都是一个模子里刻出来的。然而,由于产权问题,OpenJDK 不得不将一些被迫闭源的内容替换成了自己的开源实现。除此之外, OpenJDK 只包含了最精简的软件包,比如 Rhino , Java DB ...... 但你仍然可以选择在需要的时候将它们补充进去。

所以不用太担心,无论是何种 XX JDK ,它都应该能保证运行基本的 Java 代码,否则就不能称之为 JDK。

4504b01ce46a800fb994490f0d8265c8.png

HotSpot 虚拟机目录结构

HotSpot 是目前最通用的 Java 虚拟机,其中,HotSpot VM 的实现在 hotspot/src 目录。其结构如下所示:

  1. ├─agent Serviceability Agent的客户端实现
  2. ├─make 用来build出HotSpot的各种配置文件
  3. ├─src HotSpot VM的源代码
  4. │ ├─cpu CPU相关代码(汇编器、模板解释器、ad文件、部分runtime函数在这里实现)
  5. │ ├─os 操作系相关代码
  6. │ ├─os_cpu 操作系统+CPU的组合相关的代码
  7. │ └─share 平台无关的共通代码
  8. │ ├─tools 工具
  9. │ │ ├─hsdis 反汇编插件
  10. │ │ ├─IdealGraphVisualizer 将server编译器的中间代码可视化的工具
  11. │ │ ├─launcher 启动程序“java”
  12. │ │ ├─LogCompilation 将-XX:+LogCompilation输出的日志(hotspot.log)整理成更容易阅读的格式的工具
  13. │ │ └─ProjectCreator 生成Visual Studio的project文件的工具
  14. │ └─vm HotSpot VM的核心代码
  15. │ ├─adlc 平台描述文件(上面的cpu或os_cpu里的*.ad文件)的编译器
  16. │ ├─asm 汇编器接口
  17. │ ├─c1 client编译器(又称“C1”)
  18. │ ├─ci 动态编译器的公共服务/从动态编译器到VM的接口
  19. │ ├─classfile 类文件的处理(包括类加载和系统符号表等)
  20. │ ├─code 动态生成的代码的管理
  21. │ ├─compiler 从VM调用动态编译器的接口
  22. │ ├─gc_implementation GC的实现
  23. │ │ ├─concurrentMarkSweep Concurrent Mark Sweep GC的实现
  24. │ │ ├─g1 Garbage-First GC的实现(不使用老的分代式GC框架)
  25. │ │ ├─parallelScavenge ParallelScavenge GC的实现(server VM默认,不使用老的分代式GC框架)
  26. │ │ ├─parNew ParNew GC的实现
  27. │ │ └─shared GC的共通实现
  28. │ ├─gc_interface GC的接口
  29. │ ├─interpreter 解释器,包括“模板解释器”(官方版在用)和“C++解释器”(官方版不在用)
  30. │ ├─libadt 一些抽象数据结构
  31. │ ├─memory 内存管理相关(老的分代式GC框架也在这里)
  32. │ ├─oops HotSpot VM的对象系统的实现
  33. │ ├─opto server编译器(又称“C2”或“Opto”)
  34. │ ├─prims HotSpot VM的对外接口,包括部分标准库的native部分和JVMTI实现
  35. │ ├─runtime 运行时支持库(包括线程管理、编译器调度、锁、反射等)
  36. │ ├─services 主要是用来支持JMX之类的管理功能的接口
  37. │ ├─shark 基于LLVM的JIT编译器(官方版里没有使用)
  38. │ └─utilities 一些基本的工具类
  39. └─test 单元测试

准备编译工具并编译

我们可以通过 yum 工具快速安装编译所需的工具:

  1. $ sudo yum groupinstall "Development Tools"
  2. yum install libXtst-devel libXt-devel libXrender-devel
  3. yum install cups-devel
  4. yum install freetype-devel
  5. yum install alsa-lib-devel

在一切准备就绪之后,我们需要进入到 openjdk 的根目录下,并准备一些配置信息:

  1. $ sudo cd yourOpenJdk
  2. $ sudo bash ./configure --with-target-bits=64 --with-boot-jdk=yourBootJdk --with-debug-level=slowdebug --enable-debug-symbols ZIP_DEBUGINFO_FILES=0

下面是对参数的简单介绍:

  1. with-target-bits :指定生成的是 64 或者是 32 位的 jdk。
  2. with-boot-jdk : 指向你自己的 BootStrap JDK 的实际路径
  3. with-debug-level :编译时的 debug 级别,分为 release , fastdebug , slowdebug 三种。默认是 release。
  4. enable-debug-symbols :生成调试的符号信息。

configure 命令承担了对依赖性检查,参数配置等职责。如果某些工具或者依赖项缺失,该命令会给出明确的提示。如果 configure 命令没有发生问题,我们就可以使用 make 命令进行编译了。

$ sudo make all ZIP_DEBUGINFO_FILES=0

笔者为虚拟机分配了一个 CPU 和 3 GB 内存空间,编译过程等待了较长的时间(笔者初期仅仅分配了 1GB 内存空间,结果导致编译过程中报内存分配失败错误,因此不得不在调整虚拟机配置后重新进行编译)。在编译过程中可能会报多处 warnings,但是只要不是导致中断编译的 warnings ,一般不会影响编译结果。

此外,编译过程中控制台可能还会打印以下错误,此为编译过程本身的一个 bug,我们可以暂时将其忽略。

  1. Generating Nimbus source files
  2. Verifying /u/alanb/ws/tl/build/linux-x86_64-normal-server-release/jdk/gensrc_x11wrappers/sizes.64.verification.tmp to /u/alanb/ws/tl/build/linux-x86_64-normal-server-release/jdk/gensrc_x11wrappers/sizes.64
  3. [Error] encoded value was less than 0: encode(-8.326673E-17, 5.0, 11.0, 16.0)
  4. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  5. [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
  6. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  7. [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
  8. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  9. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  10. [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
  11. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  12. [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
  13. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  14. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  15. [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
  16. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  17. [Error] encoded value was greater than 3: encode(15.029411, 1.0, 14.0, 15.0)
  18. [Error] encoded value was less than 0: encode(-0.05882353, 1.0, 24.0, 25.0)
  19. [Error] Encountered Infinity: encode(-0.00877193, 0.0, 7.0, 7.0)

最终,编译完成后控制台会输出编译时间:

  1. ----- Build times -------
  2. Start 2020-08-12 04:26:55
  3. End 2020-08-12 04:40:23
  4. 00:00:00 corba
  5. 00:00:55 demos
  6. 00:05:08 docs
  7. 00:00:01 hotspot
  8. 00:01:47 images
  9. 00:00:01 jaxp
  10. 00:00:01 jaxws
  11. 00:04:58 jdk
  12. 00:00:02 langtools
  13. 00:00:35 nashorn
  14. 00:13:28 TOTAL

后续工作

在编译完成之后,BootStrap JDK 就完成了它的使命,在编译完成之后我们便可以卸载掉 BootStrap JDK 了。

  1. $ sudo yum list installed | grep java
  2. $ sudo yum -y remove java-1.7.0-openjdk*

编译好的 JDK 会在 ../openjdk/build/linux-x86_64-normal-server-slowdebug/jdk 文件夹下。由于编译时 config 的参数可能并不一致,因此编译后输出的文件夹名称可能也有所区别。

我们此刻再用这个编译好的 JDK 去配置 Java 环境变量:

$ sudo vim /etc/profile

增加以下配置,注意 JAVA_HOME 应当指向你实际的文件夹路径,这里仅以笔者的为准。配置完毕后不要忘记使用 source /etc/profile 刷新一下配置:

  1. JAVA_HOME=/root/openjdk/build/linux-x86_64-normal-server-slowdebug/jdk/
  2. JRE_HOME=$JAVA_HOME/jre
  3. PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
  4. CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
  5. export JAVA_HOME JRE_HOME PATH CLASSPATH

使用 java -version 打印结果,笔者这里显示:

  1. openjdk version "1.8.0_181"
  2. OpenJDK Runtime Environment (build 1.8.0_181-b13)
  3. OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)

而直接安装 JDK 的机器会显示:

  1. java version "1.8.0_144"
  2. Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
  3. Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

我们可以准备一个测试用的 JvmTest.java 文件,观察此 openJDK 是否能够正常工作:

  1. public class JvmTest {
  2. public static void main(String[] args) {
  3. System.out.println(System.getProperty("user.home"));
  4. System.out.println(System.getProperty("java.version"));
  5. System.out.println(System.getProperty("os.name"));
  6. System.out.println(System.getProperty("java.vendor.url"));
  7. }
  8. }

如果 java , javac 均没有问题,则控制台可以输出一下内容:

  1. /root
  2. 1.8.0_181
  3. Linux
  4. http://java.oracle.com/

至此,一个手动编译 JDK 的 demo 就算结束了。

来源: https:// juejin.im/post/68600883 25307547661?utm_source=tuicool&utm_medium=referral

欢迎关注微信公众号【慕容千语】

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/805432
推荐阅读
相关标签
  

闽ICP备14008679号