JVM (Java 虚拟机) 架构2025年3月5日 | 阅读7分钟 JVM (Java 虚拟机) 是一个抽象机器。它是一个规范,提供了一个可以执行 Java 字节码的运行时环境。 JVM 可用于许多硬件和软件平台(即,JVM 是平台相关的)。 JVM 被设计为平台相关的,这意味着它们是为特定的硬件和软件平台量身定制的。这种灵活性允许 JVM 实现针对各种系统优化性能和兼容性,从而使 Java 应用程序能够在不同的环境中一致运行。 什么是 JVM它是一个 - 规范,规定了 Java 虚拟机的工作方式。然而,实现提供者可以独立选择算法。Oracle 和其他公司都提供了它的实现。
- 实现。它的实现被称为 JRE (Java 运行时环境)。
- 运行时实例。每当我们在命令提示符下输入 java 命令来运行 Java 类时,就会创建一个 JVM 实例。
它的作用是什么?JVM 执行以下操作: JVM 为以下内容提供定义: - 内存区域
- 类文件格式
- 寄存器集
- 垃圾收集堆
- 致命错误报告等。
JVM 架构
1) 类加载器类加载器是 JVM 的一个子系统,用于加载类文件。每当我们运行 Java 程序时,它首先由类加载器加载。Java 中有三个内置的类加载器。 - 引导类加载器 (Bootstrap ClassLoader):它是第一个类加载器,是扩展类加载器的超类。它加载包含 Java 标准版所有类文件的 rt.jar 文件,例如 java.lang 包类、java.net 包类、java.util 包类、java.io 包类、java.sql 包类等。
- 扩展类加载器 (Extension ClassLoader):它是引导类加载器的子类加载器,也是系统类加载器的父类加载器。它加载位于 $JAVA_HOME/jre/lib/ext 目录中的 jar 文件。
- 系统/应用程序类加载器 (System/Application ClassLoader):它是扩展类加载器的子类加载器。它从类路径中加载类文件。默认情况下,类路径设置为当前目录。您可以使用 "-cp" 或 "-classpath" 开关更改类路径。它也称为应用程序类加载器。
- 运行时数据区:JVM 在程序执行期间为各种运行时数据区分配内存。这些包括方法区、堆、栈、PC(程序计数器)寄存器和本地方法栈。方法区存储类结构、方法代码、静态变量和常量池。堆是对象分配的地方,而栈保存方法调用帧。PC 寄存器跟踪当前正在执行的 JVM 指令,本地方法栈用于执行本地方法。
- 执行引擎:执行引擎负责执行编译后的 Java 字节码。它由两个组件组成:解释器和即时 (JIT) 编译器。解释器逐条读取和执行字节码指令,而 JIT 编译器将频繁执行的字节码序列编译为本机机器码以提高性能。
- 垃圾收集器:JVM 包含一个垃圾收集器,负责回收不再使用的对象占用的内存。垃圾收集器识别并移除不可达对象,从而为新分配腾出内存。有各种垃圾收集算法可用,每种算法在性能和内存开销方面都有其自身的权衡。
- 本地方法接口 (JNI):JNI 使 Java 代码能够与本地库交互并执行 Java 语言不直接支持的操作。它允许 Java 应用程序调用用 C 和 C++ 等语言编写的函数,从而提供灵活性并访问平台特定的功能。
文件名:ClassLoaderExample.java 输出 sun.misc.Launcher$AppClassLoader@4e0e2f2a
null
说明 此 Java 代码演示了如何打印给定类的类加载器名称。在 ClassLoaderExample 类的 main 方法中,我们使用 ClassLoaderExample.class 语法获取代表 ClassLoaderExample 类本身的 Class 对象的引用。然后我们在这个 Class 对象上调用 getClassLoader() 方法来检索加载 ClassLoaderExample 类的类加载器。 这个类加载器通常是系统/应用程序类加载器,因为它负责加载用户定义的类。接下来,我们使用 String.class.getClassLoader() 打印 String 类的类加载器名称。然而,这返回 null,因为 String 是 Java 标准版的一部分的内置类,存在于 rt.jar 中。由于 rt.jar 是由引导类加载器加载的,它是由本地代码编写的,因此它没有关联的 Java 类加载器对象,因此结果为 null。 这些是 Java 提供的内部类加载器。如果我们想创建自己的类加载器,我们需要扩展 ClassLoader 类。 2) 类(方法)区类(方法)区存储每个类的结构,例如运行时常量池、字段和方法数据以及方法代码。 - 运行时常量池:运行时常量池是类(方法)区的一部分,由常量池条目组成。这些用于存储符号引用、字面量以及类所需的其他常量值。这些包括类和接口名称、方法和字段名称、字符串字面量和数字字面量。
- 字段数据:类(方法)区还存储关于类中声明的字段的信息,包括它们的名称、类型和访问修饰符。JVM 在运行时使用此数据访问和操作对象字段。
- 方法数据:除了字段数据之外,类(方法)区还包含关于类中声明的方法的信息,包括它们的名称、返回类型、参数类型和字节码指令。JVM 在程序执行期间使用此信息调用方法和执行字节码指令。
3) 堆堆是分配对象的运行时数据区。 - 对象分配:堆负责为程序执行期间动态创建的对象分配内存。当使用 new 关键字或调用构造函数实例化对象时,会在堆上分配内存来存储对象的数据。
- 垃圾收集:堆由 JVM 的垃圾收集器管理,该收集器定期扫描堆以查找不再使用或应用程序不可达的对象。垃圾收集涉及回收这些未使用对象占用的内存,为新分配腾出空间。
- 堆结构:堆通常分为两个主要区域:新生代 (Young Generation) 和老年代 (Old Generation)(也称为终身代 Tenured Generation)。新生代进一步分为伊甸区 (Eden Space) 和幸存区 (Survivor Spaces),而老年代包含经过多次垃圾收集周期后仍然存活的长期对象。
4) 栈Java 栈存储帧。它保存局部变量和部分结果,并在方法调用和返回中发挥作用。 每个线程都有一个私有的 JVM 栈,与线程同时创建。 每次调用方法时都会创建一个新帧。当方法调用完成时,该帧被销毁。 - 帧结构:每个帧包含局部变量、操作数栈和对正在执行的方法的运行时常量池的引用。局部变量存储方法参数和局部变量,而操作数栈用于在方法执行期间的中间结果和操作数操作。
- 方法调用:栈在方法调用和返回中起着至关重要的作用。当调用方法时,会创建一个新帧并推入栈中。方法完成后,该帧会从栈中弹出,并将控制权返回给调用方法。
5) 程序计数器寄存器PC(程序计数器)寄存器包含当前正在执行的 Java 虚拟机指令的地址。 程序计数器 (PC) 寄存器是 JVM 内的一个特殊寄存器,它包含当前正在执行指令的内存地址。 - 指令指针:PC 寄存器充当指令指针,指导 JVM 按照正在执行的字节码指令序列进行操作。
- 线程特定:与 Java 栈一样,Java 应用程序中的每个线程都有自己的 PC 寄存器,允许多个线程并发执行指令而不相互干扰。
6) 本地方法栈它包含应用程序中使用的所有本地方法。 本地方法栈是 JVM 中的一个内存区域,用于执行本地方法,这些方法是用 Java 以外的语言编写的,例如 C 或 C++。有关本地方法栈的其他信息包括: 本地方法调用:当 Java 应用程序调用本地方法时,执行流会转换到本地方法栈,在那里执行本地方法的代码。 7) 执行引擎它包含 - 一个虚拟处理器
- 解释器:读取字节码流然后执行指令。
- 即时 (JIT) 编译器:它用于提高性能。JIT 会同时编译具有相似功能的字节码部分,从而减少编译所需的时间。在这里,“编译器”一词指的是从 Java 虚拟机 (JVM) 的指令集到特定 CPU 指令集的转换器。
8) Java 本地接口Java 本地接口 (JNI) 是一个框架,提供了一个接口,用于与用其他语言(如 C、C++、汇编等)编写的其他应用程序进行通信。Java 使用 JNI 框架将输出发送到控制台或与操作系统库交互。 |