JVM 整体架构

1. 前言

本节内容主要是介绍 JVM 的整体架构,对 JVM 有一个宏观的认识,是本套课程的基础知识部分,虽然为基础知识,但是对整体架构的理解,是学习 JVM 课程的前提,所以要重视本节的内容学习。本节主要知识点如下:

  • 认识 JVM 整体结构,对 JVM 的各个模块作用有一个初步的了解,为本节基础内容;
  • 对「类加载子系统模块」,进行更加细粒度的模块划分介绍,重点从概念层面掌握类加载的步骤,为本节重点内容之一;
  • 对「运行时数据区」进行更加细粒度的模块划分介绍,重点先从概念层面,了解运行时数据区的五大模块定义及作用,为本节重点内容之一;
  • 对「执行引擎」进行更加细粒度的模块划分介绍,重点先从概念层面,了解执行引擎的三大模块定义及作用,其中包括垃圾回收器的初步介绍,为本节重点内容之一;
  • 了解 JVM 的生命周期,可视作本节课程的次重点内容。

本节内容是我们初次了解 JVM 整体架构,以及各模块的定义及作用,从概念的角度去了解 JVM 的各个模块,为我们后续对各模块的深入学习打下了良好的基础。

2. JVM 整体架构

我们首先来看看,JVM 的整体结构图,然后对每一个结构模块进行简单的介绍。

图片描述

从结构中可以看出,JVM 结构主要分为以上几个模块,其中部分重点模块内部还会细分责任更加明细的模块,此处先来简单了解下每个模块的作用。

  • Class 文件:主要指编译成字节码的 Java 文件,Class 文件才是 JVM 可以识别的文件,所以 Java 文件需要先进行编译才可进入 JVM 执行;
  • 类加载子系统:类的加载,主要负责从文件系统,或者网络中加载 Class 信息,并与运行时数据区进行交互;
  • 运行时数据区:主要包括五个小模块,Java 堆, Java 栈,本地方法栈,方法区,寄存器。后文对细节模块会有概念的介绍;
  • 执行引擎:分配给运行时数据区的字节码将由执行引擎执行,执行引擎读取字节码并逐个执行。垃圾回收器就是执行引擎的一部分;
  • 本地方法接口:本机方法库进行交互,并提供执行引擎所需的本机库;
  • 本地方法库:它是执行引擎所需的本机库的集合。

通过上文对JVM 整体结构的介绍,我们对JVM有了一定的认识。但是对于上图所示的JVM 整体架构图,还是感觉很抽象,感觉并不直观。那么我们再继续看下图,通过 6 个步骤,来简单的描述下,一个 Java 文件在 JVM 中的流转过程。

图片描述

我们对上图中的 6 个步骤,逐一进行介绍:

  • 步骤 1 : 我们的 Demo.java 文件,通过 JDK 的 javac 命令,成功的被编译成为额 Demo.class 文件;
  • 步骤 2 :JVM 有自己的类加载器,将编译好的 Demo.class文件进行了加载;
  • 步骤 3 :类加载器将加载的 Demo.class文件投放到了运行时数据区,供程序执行使用;
  • 步骤 4 :运行时数据区将字节码文件,交给执行引擎执行;
  • 步骤 5 :执行引擎执行完毕,会对运行时数据区的数据进行操作,比如说垃圾回收机制是执行引擎的一部分,垃圾回收机制,针对的是运行时数据区的堆空间,后续我们会详细讲解;
  • 步骤 R :我们发现图中有很多步骤 R ,此处 R 代表 Random,即随机发生的步骤。其实就是我们在执行过程中的一个本地方法的调用,只要我们的程序在运行过程中需要调用本地方法,那么步骤R就会发生。

Tips:此处仅仅是一个简要的介绍,后文会对重点部分进行更加细粒度的模块介绍。此处需要同学记住整体 JVM 结构框架,方便后续知识的学习。

3. 类加载子系统

Java 的动态类加载功能由类加载器子系统处理,处理过程包括加载、链接和初始化。如下图所示,展现了类加载子系统的处理过程。

图片描述

我们来介绍下上图中类加载子系统的三个步骤:

加载:通过三种不同的类加载器对 Class 文件进行加载,后续章节会对三种类加载器单独进行讲解。我们也可以自定义类加载器,通过复写 classLoader 方法可以实现自定义的类加载器。

链接:链接阶段会对加载好的 Class 文件进行字节码、静态变量、方法引用等进行验证和解析,为初始化做准备。

初始化:类加载的最后阶段,对类进行初始化。

Tips:类加载子系统是非常复杂的,其实加载(Loading)和链接(Linking)部分还能够进行更加细致的过程划分。鉴于我们刚刚接触 JVM,此处点到即止。不过不用担心,后续的章节会对加载(Loading)和链接(Linking)这两个部分进行更加细粒度的划分以及更加细致的讲解,我们循序渐进,步步为营。

4. 运行时数据区

如下图运行时数据区共包含如下 5 个模块,方法区,Java 栈,本地方法栈,堆和程序计数器。

图片描述

Tips:方法区和堆为共享内存区域,多线程环境下共享这两块内存区域。 Java 栈,本地方法栈和程序计数器为线程私有部分,私有数据对其他线程不可见。

我们下边来介绍下运行时数据区的五个模块:

  • 方法区(Method Area):所有的类级数据将存储在这里,包括静态变量。每个 JVM 只有一个方法区,它是一个共享资源;

  • 堆区域(Heap Area):所有对象及其对应的实例变量和数组将存储在这里。每个 JVM 也只有一个堆区域。由于方法和堆区域共享多个线程的内存,所存储的数据不是线程安全的;

  • 栈区(Stack Area):对于每个线程,将创建单独的运行时栈。对于每个方法调用,将在栈存储器中产生一个条目,称为栈帧。所有局部变量将在栈内存中创建。栈区域是线程安全的,因为它不共享资源;

  • PC寄存器(PC Registers):也称作程序计数器。每个线程都有单独的 PC 寄存器,用于保存当前执行指令的地址。一旦执行指令,PC 寄存器将被下一条指令更新;

  • 本地方法栈(Native Method stacks):本地方法栈保存本地方法信息。对于每个线程,将创建一个单独的本地方法栈。

5. 执行引擎

如下图执行引擎共包含如下三个模块,解释器,JIT 编译器和垃圾回收器。

图片描述

Tips:垃圾回收器是本模块的重中之重,也是 JVM 的重中之重。后续会有专门的小节内容对垃圾回收器进行细致的讲解。

我们下边来介绍下执行引擎的三个模块:

  • 解释器:解释器是作用于字节码的解释。解释器的缺点是当一个方法被调用多次时,每次都需要一个新的解释;

  • JIT 编译器:JIT 编译器消除了解释器的缺点。执行引擎将在转换字节码时使用解释器的帮助,但是当它发现重复的代码时,将使用 JIT 编译器,这提高了系统的性能;

  • 垃圾回收器(Garbage Collector):收集和删除未引用的对象。可以通过调用 System.gc() 触发垃圾收集。

6. 小结

本节主要是对 JVM 的整体框架进行介绍,了解整体架构是学习 JVM 的基本前提。后续我们会对每一个模块进行展开讲解,所有的知识点都是围绕 JVM 的架构展开的,本节内容非常重要,一定要认真的学习。