Java 虚拟机 (JVM) 是一个复杂的软件系统,其内部结构可以划分为几个关键的功能模块。根据功能和责任的不同,主要可以分为以下几大部分:
1.类加载器子系统 (Class Loader Subsystem)
-
职责: 负责将
.class
文件(包含字节码)加载到 JVM 中,并进行链接(验证、准备、解析)和初始化。 -
关键组件:
-
加载 (Loading): 查找并加载类的二进制字节码(通常来自文件系统、网络或其他来源)。由类加载器 (ClassLoader) 实现(如
Bootstrap
、Extension
、Application/System
、Custom
加载器),遵循"双亲委派模型"。 -
链接 (Linking):
- 验证 (Verification): 确保被加载的
.class
文件符合 JVM 规范且不会危害 JVM 自身安全(如文件格式、字节码语义、符号引用等)。 - 准备 (Preparation): 为类的静态变量 分配内存(在方法区)并赋予数据类型的默认初始值。
- 解析 (Resolution): 将常量池中的符号引用(名称描述)解析为直接引用(内存地址/偏移量)。
- 验证 (Verification): 确保被加载的
-
初始化 (Initialization): 执行类的
clinit()
方法(编译器自动收集类中所有静态变量赋值动作和静态语句块static{}
合并生成),为静态变量赋予程序中指定的初始值。
-
2.运行时数据区 (Runtime Data Areas / Memory Areas)
-
职责: JVM 在执行程序时分配和管理内存的区域。这是存储程序运行所需数据的核心区域。
-
关键组件:
-
方法区 (Method Area / Metaspace in HotSpot): 存储已被加载的类信息 、常量 (Runtime Constant Pool) 、静态变量 (static variables) 、即时编译器编译后的代码等。是线程共享的区域。
-
堆 (Heap Area): 用于存放程序运行过程中创建的所有对象实例 和数组。也是垃圾回收器管理的主要区域。同样是线程共享的区域。
-
Java 虚拟机栈 (Java Virtual Machine Stacks):线程私有。每个线程在创建时都会分配一个对应的栈。栈中存储的是栈帧 (Stack Frame)。每个方法被调用时,会创建一个栈帧,栈帧中存储:
- 局部变量表 (Local Variable Array/Local Variables): 存储基本数据类型变量、对象引用 (
reference
)、returnAddress 类型。 - 操作数栈 (Operand Stack): 方法执行过程中计算的中间结果和工作空间。
- 动态链接 (Dynamic Linking): 指向运行时常量池中该栈帧所属方法的引用。
- 方法返回地址 (Return Address): 方法正常退出或异常终止时,需要返回到调用者的位置。
- 局部变量表 (Local Variable Array/Local Variables): 存储基本数据类型变量、对象引用 (
-
本地方法栈 (Native Method Stacks): 线程私有 。服务于 JVM 调用的
native
方法(非 Java 编写,通常用 C/C++ 实现)。 -
程序计数器 (Program Counter Register): 线程私有 。指向当前线程正在执行的字节码指令的地址(如果正在执行的是
native
方法,则为undefined
)。用于实现线程切换后能恢复到正确的执行位置。是 JVM 规范中唯一 不会发生OutOfMemoryError
的区域。
-
3.执行引擎 (Execution Engine)
-
职责: 负责解释执行 字节码指令或编译执行本地机器码指令。是程序实际运行的"CPU"。
-
关键组件/工作方式:
- 解释器 (Interpreter): 逐条读取、解释并执行字节码指令。启动速度快(无需编译等待),但执行速度相对较慢。
- 即时编译器 (Just-In-Time Compiler - JIT Compiler): (例如 HotSpot JVM 中的 C1、C2、Graal)将"热点代码"(频繁执行的代码段)即时编译成本地机器码(存储在方法区),然后直接执行本地机器码,大幅提高执行效率。JIT 是 JVM 性能优化的核心。解释器和 JIT 通常会配合工作(解释器启动,JIT 在运行时介入)。
- 垃圾回收器 (Garbage Collector - GC): 严格来说,GC 是执行引擎的核心组成部分之一(并且是一个极其重要的模块),运行在专门的 GC 线程上。 它负责自动管理堆内存中对象的分配 与回收,识别并清除不再被引用的对象(垃圾),释放内存空间供后续分配使用。不同的 GC 算法(如 Mark-Sweep, Mark-Compact, Copying, Generational)和收集器(如 Serial, Parallel, CMS, G1, ZGC, Shenandoah)有不同的特点和适用场景。GC 的性能对应用影响巨大。
-
本地方法接口 (Java Native Interface - JNI)
- 职责: 提供一套标准接口,使得在 JVM 内部运行的 Java 代码可以调用**外部的本地库(C/C++, Fortran 等语言编写)**中的方法,或者允许本地库调用 Java 代码。它充当了 Java 世界与非 Java 本地代码世界的桥梁。
- 作用: 用于访问操作系统或硬件的底层功能(超越 Java API 的能力)、集成遗留系统代码、提高关键部分的性能等。
-
本地方法库 (Native Method Libraries)
- 职责: 包含通过 JNI 调用的实际本地库(例如
.dll
,.so
,.dylib
文件)。这些库不是 JVM 规范的一部分,但它们是被 JNI 使用的执行基础。
- 职责: 包含通过 JNI 调用的实际本地库(例如
理解这些组成部分及其交互,是深入掌握 Java 程序运行原理、进行性能调优(特别是内存管理和 GC 调优)和问题诊断的基础。
总结图:
sql
┌──────────────────────────────────────────────────┐
│ Java Virtual Machine │
├────────────────┬────────────────┬────────────────┤
│ Class Loader │ Runtime Data │ Execution │
│ Subsystem │ Areas │ Engine │
│ ┌───────────┐ │ ┌────────────┐ │ ┌────────────┐ │
│ │ Loading │ │ │ Method Area│ │ │ Interpreter│ │
│ └───────────┘ │ ├────────────┤ │ └────────────┘ │
│ ┌───────────┐ │ │ Heap │ │ ┌────────────┐ │
│ │ Linking: ├──┼─┤ (Objects) │←┼─┤ JIT │ │
│ │ Verify │ │ ├────────────┤ │ │ Compiler │ │
│ │ Prepare │ │ │ Java Stack │ │ └────────────┘ │
│ │ Resolve │ │ ├────────────┤ │ ┌────────────┐ │
│ └───────────┘ │ │ Native Mtd│ │ │ Garbage ├─┘
│ ┌───────────┐ │ │ Stack │ │ │ Collector │ GC
│ │Initialize │ │ ├────────────┤ │ └────────────┘ Threads
│ └───────────┘ │ │ PC Registers│ │
└────────────────┴─┴────────────┴─┴────────────────┘
↑ ↑ ↑ ↑ ↑
↓ │ │ │ ↓
┌───────┴───────┐ │ │ │ ┌───────┴───────┐
│ .class Files │ │ │ │ │ Native Method │
│ (Bytecode) │ │ │ │ │ Libraries │
└───────────────┘ │ │ │ └───────┬───────┘
│ │ │ │
┌────┘ └──┼───Java Native│Interface(JNI)──┐
│ Thread 1 │ │ │
│ Thread 2 │ │ │
│ ... │ │ │
└──────────┘ └────────────────┘