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

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 的核心内容 —— 虚拟机。

openjdk 
—— corba:多语言、分布式通讯接口 
—— hotspot:Java 虚拟机 
—— jaxp:XML 处理 
—— jaxws:一组 XML web services 的 Java API 
—— jdk:java 开发工具包 
—— langtools:Java 语言工具 
—— 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 目录。其结构如下所示:

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

准备编译工具并编译

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

$ sudo yum groupinstall "Development Tools"  
yum install libXtst-devel libXt-devel libXrender-devel  
yum install cups-devel  
yum install freetype-devel   
yum install alsa-lib-devel  

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

$ sudo cd yourOpenJdk
$ 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,我们可以暂时将其忽略。

Generating Nimbus source files
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
[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)

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

----- Build times -------
Start 2020-08-12 04:26:55
End   2020-08-12 04:40:23
00:00:00 corba
00:00:55 demos
00:05:08 docs
00:00:01 hotspot
00:01:47 images
00:00:01 jaxp
00:00:01 jaxws
00:04:58 jdk
00:00:02 langtools
00:00:35 nashorn
00:13:28 TOTAL

后续工作

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

$ sudo yum list installed | grep java
$ 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 刷新一下配置:

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

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

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

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

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

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

public class JvmTest {  
 public static void main(String[] args) {  
  System.out.println(System.getProperty("user.home"));  
  System.out.println(System.getProperty("java.version"));  
  System.out.println(System.getProperty("os.name"));  
  System.out.println(System.getProperty("java.vendor.url"));  
 }  
}

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

/root
1.8.0_181
Linux
http://java.oracle.com/

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK1.7新特性介绍 1. 对Java集合(Collections)的增强支持 在JDK1.7之前的版本中,Java集合容器中存取元素的形式如下: 以List、Set、Map集合容器为例: // 创建List接口对象 List<String> list = new ArrayList<String>(); list.add("item") String item = list.get(0); // 创建Set接口对象 Set<String> set = new HashSet<String>(); set.add("item"); // 创建map接口对象 Map<String, Integer> map = new HashMap<String, Integer>(); map.put("item", 1); int val = map.get("item");12345678910111213 在JDK1.7中,摒弃了Java集合接口的实现类,如:ArrayList、HashSet和HashMap。而是直接采用[]、{}的形式存入对象,采用[]的形式按照索引、键值来获取集合中的对象,如下: List<String> list = ["item"]; // 向List集合里面添加元素 String item = list[0]; Set<String> set = {"item"}; // 向Set集合里面添加元素 Map<String, Integer> map = {"item", 1}; // 向map集合里面添加元素 int val = map{"item"};1234567 2. 在Switch中可用String String s = ... switch(s) { case "item1": process1(); case "item2": process2(); break; . . . default: processn(); break; }1234567891011121314 3. 数值可加下划线 例如:int one_million = 1_000_000; 4. 支持二进制文字 例如:int binary = 0b1001_1001; 5. 简化了可变参数方法的调用 当程序员试图使用一个不可具体化的可变参数并调用一个varargs (可变)方法时,编辑器会生成一个“非安全操作”的警告。 --------------------- 本文来自 烦人的萝卜籽 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/jiangliwe/article/details/76026546?utm_source=copy
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值