JVM (Java Virtual Machine) 架构
什么是 JVM (Java Virtual Machine)?
JVM (Java Virtual Machine) 是一个虚拟机,它是一个抽象计算机,具有自己的指令集体系结构(ISA)、内存、栈、堆等。它运行在宿主操作系统上,并向其提出资源需求。
JVM (Java Virtual Machine) 是一个规范,可以有不同的实现,只要它们遵守该规范即可。该规范可以在以下链接中找到 − https://docs.oracle.com
Oracle 有自己的 JVM 实现(称为 HotSpot JVM),IBM 也有自己的实现(例如 J9 JVM)。
规范中定义的操作如下所示(来源 − Oracle JVM Specs)
- 'class' 文件格式
- 数据类型
- 原始类型和值
- 引用类型和值
- 运行时数据区
- 帧
- 对象的表示
- 浮点运算
- 特殊方法
- 异常
- 指令集摘要
- 类库
- 公共设计,私有实现
JVM (Java Virtual Machine) 架构
HotSpot JVM 3 的架构如下图所示 −
执行引擎包括垃圾收集器和 JIT 编译器。JVM 有两种变体 − client 和 server。两者共享相同的运行时代码,但使用的 JIT 不同。我们稍后将详细了解这一点。用户可以通过指定 JVM 标志 -client 或 -server 来控制使用哪种变体。server JVM 专为服务器上长时间运行的 Java 应用程序而设计。
JVM 有 32 位和 64 位版本。用户可以通过在 VM 参数中使用 -d32 或 -d64 来指定使用哪个版本。32 位版本最多只能寻址 4G 内存。对于关键应用程序在内存中维护大型数据集,64 位版本满足了这一需求。
JVM(Java 虚拟机)架构的组件
以下是 JVM(Java 虚拟机)架构的主要组件:
1. Class Loader
JVM 以动态方式管理类和接口的加载、链接和初始化过程。在加载过程中,JVM 找到类的二进制表示并创建它。
在链接过程中,已加载的类被合并到 JVM 的运行时状态中,以便在初始化阶段执行。 JVM 主要使用存储在运行时常量池中的符号表来进行链接过程。初始化包括实际执行已链接的类。
以下是 class loader 的类型:
- BootStrap class loader:这是 class loader 层次结构的最顶层。它加载 JRE 的 lib 目录中的标准 JDK 类。
- Extension class loader:这是 class loader 层次结构的中层,是 bootstrap class loader 的直接子加载器,它加载 JRE 的 lib\ext 目录中的类。
- Application class loader:这是 class loader 层次结构的最底层,是 application class loader 的直接子加载器。它加载由 CLASSPATH ENV 变量指定的 jars 和类。
2. Linking and Initialization
链接过程包括以下三个步骤 −
- Verification − 这由 Bytecode verifier 执行,以确保生成的 .class 文件(字节码)有效。如果无效,则抛出错误并停止链接过程。
- Preparation − 为类的所有静态变量分配内存,并用默认值初始化它们。
- Resolution − 将所有符号内存引用替换为原始引用。为实现这一点,使用类的方法区域中的运行时常量内存中的符号表。
Initialization 是类加载过程的最后阶段。静态变量被赋予原始值,并执行静态块。
3. Runtime Data Areas
JVM 规范定义了程序执行期间所需的某些运行时数据区域。其中一些在 JVM 启动时创建。其他的是线程本地的,仅在线程创建时创建(线程销毁时销毁)。以下是这些区域 −
PC (Program Counter) Register
它是每个线程本地的,包含线程当前执行的 JVM 指令的地址。
Stack
它是每个线程本地的,用于在方法调用期间存储参数、局部变量和返回地址。如果线程要求超过允许的栈空间,则可能发生 StackOverflow 错误。如果栈是动态可扩展的,仍可能抛出 OutOfMemoryError。
Heap
它在所有线程之间共享,包含运行时创建的对象、类的元数据、数组等。它在 JVM 启动时创建,并在 JVM 关闭时销毁。您可以使用某些标志(稍后详述)控制 JVM 从 OS 请求的 heap 量。需要注意不要要求过少或过多的内存,因为这会影响性能。此外,GC 管理此空间并持续移除死对象以释放空间。
Method Area
此运行时区域对所有线程是通用的,在 JVM 启动时创建。它存储每个类的结构,如常量池(稍后详述)、构造函数和方法的代码、方法数据等。JLS 未指定此区域是否需要垃圾回收,因此 JVM 实现可能选择忽略 GC。此外,此区域可能根据应用程序需求扩展或不扩展。JLS 未对此做出规定。
Run-Time Constant Pool
JVM 维护一个每个类/每个类型的數據结构,在链接已加载的类时充当符号表(这是其众多角色之一)。
Native Method Stacks
当线程调用 native method 时,它进入一个新世界,在这个世界中,Java 虚拟机的结构和安全限制不再限制其自由。Native method 很可能可以访问虚拟机的运行时数据区域(取决于 native method interface),但也可以做其他任何事情。
4. Execution Engine
Execution engine 负责执行字节码,它有三个不同组件:
Garbage Collection
JVM 管理 Java 中对象的整个生命周期。一旦对象创建,开发者无需再担心它。如果对象变为死对象(即不再有引用指向它),GC 将使用多种算法之一(如 serial GC、CMS、G1 等)将其从 heap 中移除。
另读: Java 中的 Garbage Collection
在 GC 过程中,对象在内存中移动。因此,在过程进行期间,这些对象不可用。整个应用程序必须在过程持续时间内停止。这种暂停称为 'stop-the-world' 暂停,是巨大的开销。GC 算法的主要目标是减少此时间。
Interpreter
Interpreter 解释字节码。它快速解释代码,但执行速度较慢。
JIT Complier
JIT 代表 Just-In-Time。JIT compiler 是 Java 运行时环境的主要部分,它在运行时将字节码编译为机器码。
5. Java Native Interface (JNI)
Java Native Interface (JNI) 与 native method 库交互,这些库对执行至关重要。
6. Native Method Libraries
Native method libraries 是 C 和 C++ 库(native libraries)的集合,这些库对执行至关重要。