JVM的类加载过程是Java日常开发中不可或缺的一环。本次课程主要讲解JVM的类加载过程。在程序运行过程中,当需要使用某个类时,JVM会通过类加载器来加载。该阶段主要负责将类的class文件加载到内存中,并创建一个代表这个类的class对象。在加载阶段,主要包括以下几个步骤。
1.加载
通过类的权限定名获取定义此类的二进制字节流。
2.连接
又包括验证、准备和解析三个阶段。
2.1验证
在验证阶段主要是对字节流进行验证,确保其符合JVM规范,不会对JVM造成危害。
验证阶段主要包括以下几个方面的验证文件:
(1)格式验证:验证字节流是否符合Class文件格式规范。
(2)元数据验证,对类的元数据信息进行验证,确保其符合语言规范。
(3)字节码验证,对字节码流进行验证,保证其符合JVM规范。
(4)符号引用验证,对类中的符号引用进行验证,确保其能正确解析为已有的类型、字段或方法。
2.2准备
JVM类加载过程中的重要步骤之一,它为类的静态变量分配内存空间,并将这些变量初始化为默认值,为后续的初始化阶段做准备。
在准备阶段,JVM会为类的静态变量分配内存空间,这些静态变量通常被存储在方法区中。与实例变量不同,静态变量属于类而不是对象,因此它们的内存空间在类加载时就已经被分配。除了分配内存空间外,JVM还会对这些静态变量进行默认初始化。默认初始化是指JVM会根据静态变量的类型,为其赋予一个默认值。对于基本数据类型,例如int、float boolean等,其默认值通常为0或者false。对于引用类型,其默认值为null。
需要注意的是,在准备阶段JVM只会为静态变量分配内存空间并进行默认初始化,而不会执行静态变量的初始化表达式。这意味着即使静态变量在类中定义时有赋值语句,也不会在准备阶段执行。
2.3解析
其主要任务是将类、接口、字段和方法的符号引用转换为直接引用,以加快后续的访问速度。
在解析阶段,JVM会对类、接口、字段和方法的符号引用进行解析,将这些符号引用转换为直接引用。符号引用是在编译阶段生成的一种标识,用来描述被引用的类、接口、字段或方法,但并不指向具体的内存地址,而直接引用则是指向具体内存地址的指针或偏移量,可以直接被JVM使用。
解析阶段的主要工作包括:
(1)将类和接口的符号引用解析为直接引用,进而快速定位到目标类或接口。
(2)将字段的符号引用解析为直接引用,进而快速定位到目标字段并访问其值。
(3)将方法的符号引用解析为直接引用,进而快速定位到目标方法,并执行相应的代码。
通过解析阶段JVM可以将类、接口、字段和方法的符号引用转换成直接引用,这样在程序运行时就可以直接使用这些引用,而不需要每次都进行符号解析,提高了程序的运行效率。
需要注意的是,解析阶段并不是必需的,可以选择在需要的时候进行解析,也可以选择在加载阶段或者初始化阶段进行解析。这取决于具体的实现方式和优化策略。
3.初始化
JVM类加载过程中的最后一个阶段,也是类加载过程中最重要的一环。
在初始化阶段,JVM会执行类构造器方法,对类的静态变量进行初始化赋值,执行静态初始化块中的代码以完成类的初始化工作。
首先,初始化阶段会执行类构造器方法。该方法是编译器自动生成的,用于对类的静态变量进行初始化。这个方法会按照静态变量的声明顺序执行,并且在多线程环境下保证线程安全。
其次,初始化阶段会执行静态初始化块中的代码。静态初始化块是在类加载时执行的一段代码,它可以用于对静态变量进行复杂的初始化操作,或执行一些其他需要在类加载时完成的任务。
需要注意的是初始化阶段是按照初始化顺序依次执行的,并且只会执行一次,如果一个类已经被初始化过了,那么在后续的加载过程中,不会再次执行初始化阶段,即使有多个类加载器加载了相同的类也是如此。初始化阶段的目的是确保类的静态变量被正确初始化,并且执行一些必要的初始化操作,使类可以正常使用。此时,类加载过程才真正完成,可以安心的使用。
通过上面的分析,可以看出JVM的类加载过程包括加载、验证、准备、解析和初始化五个阶段,每个阶段都有着特定的任务和作用,保证了类的正确加载和使用。对于JAVA程序员来说,了解JVM的类加载过程,可以更好地理解JAVA程序的运行机制,提高编程水平。