HotSpot基础

1.1 初识JVM

JVM是Java Virtual Machine(Java虚拟机)的缩写,目前市面上能见到的JVM是HotSpot和J9。在说JVM之前,需要介绍一下JDK,如下图所示。

我们下载下来的JDK8的包中会包含有一些重要的东西 ,如JRE和开发工具包Development,JRE中又包含有JVM和Libraries。其中的JDK1.8.0_36版本的目录如下:

其中的JRE就在jre目录下,jre目录具体了Java运行的所有必要依赖,体积和JDK比起来会小一些,有些Java应用会内置这个体积相对较小的jre,这样就可以直接启动应用而无需再单独下载JDK了。

这个jre/lib/rt.jar是用Java写的Libraries,如我们平时调用的核心类库中的容器、锁等的实现。

jre/lib/amd64位下有许多的动态链接库,这些链接库许多可以支持实现Java中native方法的实现,如libnet.so、libnio.so,在jre/lib/amd64/server下有一个libjvm.so,这是HotSpot虚拟机的主体实现,我们研究的所有HotSpot VM的代码实现最终都打包成了这个链接库并放到了这里。

在jdk/lib目录下有2个jar包tools.jar是用来支持各种开发工具的实现的,如jcmd、jstack、jinfo等,而sa-jdi.jar是支持调试的。

对于JDK来说,目前许多公司都提供,不过我们需要知道OpenJDK、Oracle和J9即可。

OpenJDK是一个开源(GPL)项目,提供了Java 的参考实现,该项目由Oracle领导和支持,并为Oracle自己的Java版本的发布提供了基础。 Oracle公司提供的JDK基于OpenJDK。几乎所有对Oracle Java的改动都以向OpenJDK公共仓库提交的方式开始的。 IBM公司的J9和Oracle公司一样,其开源版本是OpenJ9,而自己也会有自己的IBM J9。 国内的华为、阿里和腾讯都有自己的JDK,这些JDK都是基于OpenJDK来开发的。

从虚拟机的角度来看,OpenJDK与OracleJDK中使用的是HotSpot虚拟机,这也是目前市场上占据份额最大,最常见的虚拟机,另外就是IBMJ9和OpenJ9中的J9虚拟机了,其实这两种JDK的上层类库是共用的,只是虚拟机不同而已,所以我们用Java实现的应用程序,在OpenJDK上能跑,在OpenJ9上照样能跑,因为所调用的Java类库是同一个,不过性能跑出来差异,那就是虚拟机的问题。

我们重点研究的是HotSpot VM,其结构如下图所示。

下面简单介绍一下各个部分:

前端编译器,前端编译器主要将Java源代码转换为Class文件的字节码,例如Javac。

类加载器,在JVM启动时或者在类运行时将需要的Class文件加载到JVM中;

执行引擎,执行引擎负责执行Class文件中包含的字节码;

内存空间,将JVM需要的内存划分成若干个区。栈区存放函数的参数值,局部变量值等,会自动分配释放;堆区用于存放对象和数组,由JVM的垃圾回收器管理;方法区存放方法的元数据,占用的内存也会在必要时由垃圾回收器进行回收;

垃圾收集器,JVM使用垃圾收集器对内存进行自动管理,主要就是自动回收一些不再使用的对象,以达到释放内存的目的;

本地方法接口,调用C或C++实现的本地方法库中的方法并返回结果。

下面详细介绍一下JVM中几个重要的组成部分。

1、Javac前端编译器

我的书《深入解析Java编译器:源码剖析与实例详解》一书中对Javac这个前端编译器的源代码进行了深入剖析。这个编译器是将Java源代码翻译为Class字节码。主要的过程如下图所示。

2、类加载

类加载器主要负责类的装载。主要有启动类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)和应用类加载器(Application ClassLoader)。每个被JVM装载的类型都有一个对应的java.lang.Class类的实例来表示该类型,这个实例和其他类的实例一样存放在Java堆中。

类加载器将Class文件加载到虚拟机中时,会发生类装载、链接和初始化的工作。如下图所示。

3、执行引擎

执行引擎是JVM的核心部分,作用就是执行Class文件中的字节码指令。在Java虚拟机规范(The Java Virtual Machine Specification,JVMS)中详细定义了执行引擎遇到每条字节码指令时如何处理以及最终处理的结果,但是并没有规定执行引擎应该采取什么方式处理而得到这个结果。执行引擎具体采取什么方式由JVM的实现厂家去实现。对于OpenJDK实现厂商来说,主要采用了解释执行和编译执行来实现。

每个Java线程就是一个执行引擎的实例,在一个JVM实例中同时有多个执行引擎在工作,这些执行引擎有的在执行用户的程序,有的在执行JVM 内部的程序。

执行引擎中包括解释执行、C1编译器和C2编译器的编译执行。

解释器执行设置到的部分如下图所示。

C1编译器的编译流程如下图所示。

C2编译器的流程如下图所示。

4、内存区

执行引擎在执行Class文件的字节码时需要存储相关的信息,如操作码需要的操作数,操作码的执行结果。Class文件的字节码和涉及到的类的对象等信息都需要在执行引擎执行之前准备好。从图1-1中可以看出,一个JVM实例主要由方法区、Java堆、Java栈、本地方法栈和PC寄存器构成。其中方法区和Java 堆是所有线程共享的,也就是可以被所有的执行引擎实例访问。每个新的执行引擎实例被创建时会为这个执行引擎创建一个Java 栈,如果当前正在执行一个Java方法,那么在当前的这个Java栈中保存的是该线程中方法调用的状态,包括方法的参数、局部变量、返回值等。PC寄存器用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,所以PC寄存器也是线程私有的。

5、垃圾收集器

垃圾收集器一般完成2件事,标记不再使用的对象和回收这些无用对象。不同厂商以及相同厂商不同JVM版本提供的垃圾收集器可能不同,对于OpenJDK8来说,主要包含的垃圾收集器如图1-2所示。

图中的连线表示可以使用这2种收集器来分别收集年轻代和老年代,而G1收集器即可以收集年轻代,也可以收集老年代。

posted @ 2025-08-19 11:20  CharyGao  阅读(124)  评论(0)    收藏  举报