我们都知道 Java 是一门一次编译到处运行的语言,这与 JVM 有很大的关系,当我们执行 Java 程序的时候,会把一个一个的 .java 文件转化成 JVM 可以理解的 class 文件,然后又 JVM 去执行,注意这里的 JVM 是分操作系统的,所以可以做到一次编译,到处运行。那么Java 从源码文件(.java)到代码执行过程发生了什么?简单总结就是四个步骤:编译 --> 加载 --> 解释 --> 执行,下面我们展开来看。
编译阶段可以总结为:将 Java 的源文件(.java)编译成 JVM 可以理解的 calss 文件。
编译的过程会对源文件进行语法分析,语义分析,注解处理等一些处理。
加载阶段就是将编译好的 class 文件加载的 JVM 上。加载阶段也可以分为三步:装载 --> 连接 --> 初始化
装载阶段要做的事可以总结为:查找并加载类的二进制文件,在堆内存中创建一个 java.lang.Class 类的对象,并将类的相关信息存储在方法去中。
装载阶段并不是一次性全部装载到 JVM 的,是在需要时才装载,可以节省内存的开销。
class 文件是通过类加载器装载到 JVM 中的,所以为了避免出现两份相同的字节码文件,JVM 通过双亲委派机制去加载类的,当这个类加载器收到类加载请求时,不会自己去加载类,而是将这个类加载请求转发给他的父类加载器,让其父类加载器加载。
类加载器一般分为:
- 根加载器(BootStrap Loader):加载 JDK 中的一些本地方法。
- 扩展类加载器(ExtClassLoader):JDK 中内部实现的扩展类。
- 系统加载器(AppClassLoader):程序中的类文件。
连接阶段总结为:验证类的合法性并对其静态变量分配内存赋默认初始值。连接阶段可以分为三步:验证 --> 准备 --> 解析。
验证:验证这个类是否符合 Java 的规范,是否符合 JVM 的规范。
准备:为类的静态变量分配内存并赋予其系统默认的初始值。
解析:将符号引用转化为直接引用的过程。
初始化过程就是为类的静态变量赋予真正的初始值。
过程大概为查找类的静态变量,静态代码块,静态方法随后从上往下开始进行。
解释阶段可以总结为:将字节码解释成操作系统可以识别的指令。
在解释阶段可以有两种方法将字节码解释成操作系统可以理解的指令,分别是解释器解释和及时编译器(JIT)
,JIT 及时编译将热点代方法的指令缓存起来,当下次调用这个方法时无需重复进行解释。
执行阶段:操作系统将解析出来的指令,调用系统的硬件执行最终的程序指令。