一文读懂JVM架构解析

每个 Java 开发人员都知道字节码经由 JRE(Java运行时环境)执行。但他们或许不知道 JRE 其实是由 Java虚拟机(JVM)实现,JVM分析字节码,解释并执行它。作为开发人员,了解 JVM 的架构是非常重要的,因为它使我们能够编写出更高效的代码。

本文中,我们将深入了解 Java 中的 JVM 架构和 JVM 的各个组件。

首先我们介绍一下 JDK、JRE 以及 JVM 之间的关系。

Java 架构

Java architecture (image:javacodemonk.com)
Java 架构包括上图中提到的 3 个主要组件:

  • Java 开发工具包 (JDK)
  • Java 运行时环境(JRE)
  • Java 虚拟机 (JVM)

1、Java 开发工具包 (JDK)
JDK(Java Development Kit) 是整个 Java 的核心,通常称为 JRE 的超集。它是支持 Java 应用程序和 Java 小程序开发的基础组件。

它是特定于平台的,因此每个操作系统(例如,Mac、Unix 和 Windows)都需要单独的安装程序。

JDK 包括了 Java 运行环境 JRE(Java Runtime Envirnment),一堆 Java 工具(javac/java/jdb等)和 Java 基础的类库。

2、Java 运行时环境 (JRE)
JRE (Java Runtime Environment) 是 JDK 的一部分,包含 JVM 标准实现及 Java 核心类库。JRE 是 Java 运行环境,并不是一个开发环境,所以没有包含任何开发工具(如编译器和调试器)。

3、Java 虚拟机 (JVM)
JVM(Java Virtual Machine)是整个Java 实现跨平台的最核心的部分,能够运行以Java 语言写作的,编译完成的 class 程序。屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 Java 虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

三者的联系如下图所示:

在这里插入图片描述

JVM

JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机。它能识别 .class后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。

一般情况下,使用 C++ 开发的程序,编译成二进制文件后,就可以直接执行了,操作系统能够识别它;但是 Java 程序不一样,使用 javac 编译成 .class 文件之后,还需要使用 Java 命令去主动执行它,操作系统并不认识这些 .class 文件。

为什么不能像 C++ 一样,直接在操作系统上运行编译后的二进制文件呢?而非要搞一个处于程序与操作系统中间层的虚拟机呢?

大家都知道,Java 是一门抽象程度特别高的语言,提供了自动内存管理等一系列的特性。这些特性直接在操作系统上实现是不太可能的,所以就需要 JVM 进行一番转换。

在这里插入图片描述
有了 JVM 这个抽象层之后,Java 就可以实现跨平台了。JVM 只需要保证能够正确执行 .class 文件,就可以运行在诸如 Linux、Windows、MacOS 等平台上了。

用一句话概括 JVM 与操作系统之间的关系:JVM 上承开发语言,下接操作系统,它的中间接口就是字节码。

Java 虚拟机和字节码存储格式是实现语言无关性的基础。Java虚拟机不与包括Java语言在内的任何程序语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联,Class文件中包含了Java虚拟机指令集、符号表以及若干其他辅助信息。

在 Java 中编译器将 Java 文件编译为 .class 文件,然后将 .class 文件输入到 JVM 中,JVM 执行类文件的加载和执行的操作。

JVM 架构图
在这里插入图片描述

JVM是如何工作的?

如上图所示,JVM 中主要有三个子系统

  • 类加载器子系统(Class Loader Sub System)
  • 运行时数据区(Runtime Data Area)
  • 执行引擎(Execution Engine)

类加载器子系统

Java 虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的加载机制。

在这里插入图片描述
类加载子系统作用

  • 类加载子系统负责从文件系统或者网络中加载 class 文件,class 文件在文件开头有特定的文件标识(0xCAFEBABE)。
  • 类加载器(Class Loader)只负责 class 文件的加载,至于它是否可以运行,则由执行引擎(Execution Engine)决定。
  • 加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)。
  • Class 对象是存放在堆区的。

类加载过程:

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。(验证、准备和解析又统称为连接,为了支持Java语言的运行时绑定,所以解析阶段也可以是在初始化之后进行的。以上顺序都只是说开始的顺序,实际过程中是交叉的混合式进行的,加载过程中可能就已经开始验证了)。
在这里插入图片描述

运行时数据区

在这里插入图片描述
运行时数据区可分为5个主要组件:

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

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

  3. 虚拟机栈区(VM Stack):对于每个线程,将创建单独的运行时虚拟机栈。对于每个方法调用,将在虚拟机栈中产生一个条目,称为栈帧。所有局部变量将在堆栈内存中创建。堆栈区域是线程安全的,因为它不共享资源。堆栈框架分为三个子元素:

    • 局部变量数组(Local Variable Array):与方法相关,涉及局部变量,并在此存储相应的值
    • 操作数栈(Operand stack):如果需要执行任何中间操作,操作数堆栈将充当运行时工作空间来执行操作
    • 帧数据(Frame Data):对应于方法的所有符号存储在此处。在任何异常的情况下,捕获的区块信息将被保持在帧数据中;
  4. 程序计数器(Program Counter Register):每个线程都有单独的 PC 寄存器,用于保存当前执行指令的地址,一旦执行指令,PC寄存器将被下一条指令更新;

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

执行引擎

执行引擎主要用来执行 Java 生成 .class 的字节码,解析/编译成各种 cpu 所能执行的二进制指令。

简单来说,JVM 中的执行引擎充当了将高级语言翻译为机器语言的译者。

执行引擎主要具有三个用于执行 Java 类的主要组件:

在这里插入图片描述

解释器(interpreter):Java虚拟机启动时,会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容编译为对应平台的本地机器指令执行。

  • 解释器真正意义上所承担的角色就是一个运行时「翻译者」,将字节码文件中的内容「翻译」为对应平台的本地机器指令执行。
  • 当一条字节码指令被解释执行完成后,接着再根据 PC 寄存器中记录的下一条需要被执行的字节码指令执行解释操作。

Interpreter 的主要缺点是当多次调用同一个方法时,每次都需要新的解释,这会降低系统的性能。 所以这就是 JIT 编译器将与解释器并行运行的原因。

JIT (Just In Time Compiler):即时编译器,虚拟机将字节码直接编译成和本地机器平台相关的机器语言。(把热点代码编译成机器语言,编译慢,执行快)

即时编译的目的是避免函数被解释执行,而是将整个函数体编译成为机器码,每次函数执行时,只执行编译后的机器码即可,这种方式可以使执行效率大幅度提升。

JIT的构成组件为:

  • 中间代码生成器(Intermediate Code Generator):生成中间代码
  • 代码优化器(Code Optimizer):负责优化上面生成的中间代码
  • 目标代码生成器(Target Code Generator):负责生成机器代码或本地代码
  • 分析器(Profiler):一个特殊组件,负责查找热点,即该方法是否被多次调用;

垃圾收集器(Garbage Collector):收集和删除未引用的对象。可以通过调用 System.gc() 触发垃圾收集,但不能保证执行。JVM 的垃圾回收对象是已创建的对象。

Java本地方法接口(JNI):JNI将与本机方法库进行交互,并提供执行引擎所需的本机库。

本地方法库(Native Method Libraries):它是执行引擎所需的本机库的集合。

如果你还想看更多优质原创文章,欢迎关注我的公众号「ShawnBlog」。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值