当我们在终端键入
javac
命令,随后执行java
命令,背后其实发生了一连串复杂而精妙的过程。从源代码到可执行的字节码,再到JVM中的运行,这个过程充满了各种优化和设计哲学。在本篇博文中,主要是对JVM整体架构进行介绍,以帮助读者对JVM有个初步的整体上的理解,为后续深入学习JVM提供基础。
1 JVM 整体架构
JVM是由类加载器子系统、运行时数据区、执行引擎、本地方法接口(JNI)以及本地方法库协同工作构成的复杂系统,为Java程序提供了一个高效、安全、稳定的运行环境。
1.1 类加载器子系统
类加载器子系统是JVM中负责加载.class文件的组件。当Java程序运行时,类加载器会根据程序的需求,将相应的类加载到JVM中。这个过程包括三个主要步骤:
- 加载(Loading):查找并读取
.class
文件,创建相应的java.lang.Class
对象。 - 连接(Linking):包括验证(Verification)、准备(Preparation)和解析(Resolution)。验证确保字节码的正确性和安全性,准备为静态变量分配内存并初始化默认值,将类、接口、字段和方法的符号引用转换为直接引用。
- 初始化(Initialization):执行类构造器
<clinit>()
方法,这通常是在类中静态变量赋值和静态代码块执行时进行的。
1.2 执行引擎
执行引擎是JVM中的另一个关键组件,它负责执行加载到JVM中的字节码。执行引擎的工作包括以下几个方面:
- 字节码解释器:逐行解释执行字节码,这是最基本的执行方式。
- 即时编译器(JIT):将热点代码(经常执行的代码)编译成机器码,以提高执行效率。
- 垃圾收集器:自动管理内存,回收不再使用的Java对象,释放内存资源。
1.3 运行时数据区
运行时数据区是JVM中用于存储程序运行时数据的内存区域。它包括以下几个部分:
- 方法区:存储已被加载的类信息,如类的静态变量、常量池等。
- 堆:存储Java对象和数组,是垃圾回收的主要区域。
- 栈:存放局部变量和部分结果,并在方法调用时用于存储临时数据。
- 程序计数器:存储指向下一条指令的地址,即将要执行的字节码指令。
- 本地方法栈:为JVM使用到的本地方法(如JNI调用的C或C++方法)提供内存空间。
1.4 本地方法接口(JNI)
JNI是Java平台的一个标准,它提供了一种机制,使得Java代码可以调用本地方法库中的方法。JNI定义了一组规则和数据类型,用于在Java代码和非Java代码之间进行交互。
1.5 本地方法库
本地方法库是包含用非Java语言(如C或C++)实现的本地方法的集合。这些方法可以被Java代码通过JNI调用。
2 组件间的协同工作
JVM的各个组件之间需要紧密协作,以确保程序的顺利执行。例如,当一个Java类需要被加载时,类加载器子系统会负责加载该类,然后执行引擎会执行该类的字节码。在执行过程中,执行引擎可能会创建新的Java对象,这些对象会被存储在堆中。当对象不再被引用时,垃圾回收器会负责回收这些对象占用的内存。
此外,如果Java程序需要调用本地方法,JNI会介入,将Java代码的调用请求转发给本地方法库。在本地方法执行完毕后,JNI会将结果返回给Java程序。
3 结论
JVM是Java程序运行的基础,它的各个组件相互协作,共同确保了Java程序的高效、安全运行。了解JVM的工作原理对于Java开发者来说非常重要,它有助于我们编写更高效、更健壮的Java代码。在后续的博文中,我将进一步分享JVM的各个方面,包括类加载机制、垃圾回收、JIT编译器优化等高级主题。