# 类加载机制
# 简要回答
- 类加载过程可以分为加载、链接和初始化三个阶段,其中链接阶段可以划分为验证、准备和解析三个子阶段。
- 加载(Loading)
- 在加载阶段,类加载器负责查找类的字节码文件并将其加载到内存中。
- 链接(Linking)
- 包含三个子阶段:验证、准备、解析
- 验证(Verification):在验证阶段,会确保加载的类文件格式正确,并且不包含不安全的构造。
- 准备(Preparation):在准备阶段,在内存中为类的静态变量分配内存空间,并设置默认初始值。
- 解析(Resolution):在解析阶段,会将类、接口、字段和方法的符号引用解析为直接引用,也就是内存地址。
- 初始化(Initializing)
- 在初始化阶段,执行类的静态初始化代码,包括静态字段的赋值和静态代码块的执行。静态初始化在类的首次使用时进行,可以是创建实例、访问静态字段或调用静态方法。
# 详细回答
- 加载(Loading)
- 根据一个类的全限定名来获取字节码文件的二进制字节流,然后将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,随后在堆内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问接口。
- 链接(Linking)
- 包含三个子阶段:验证、准备、解析
- 验证(Verification):确保Class文件中字节流包含的信息符合当前虚拟机要求且不会危害自身,验证文件格式、元数据、字节码和符号引用。
- 文件格式校验是验证字节流是否符合class文件规范并且能够被当前版本的虚拟机处理。
- 元数据验证是对字节码描述的信息进行语义分析,保证其信息符合Java语言规范的要求。
- 字节码验证会进行数据流和控制流分析,保证类不会危害虚拟机安全。
- 符号引用验证是发生在解析阶段,保证解析阶段可以正常执行。
- 准备(Preparation):在准备阶段,在内存中为类的静态变量分配内存空间,并设置默认初始值为0、null或者false。如果是静态常量,编译时会被存入调用类的常量池,准备阶段不分配内存。
- 解析(Resolution):在解析阶段,虚拟机将常量池内的符号引用替换为直接引用,也就是得到类、字段、方法在内存中的指针或者偏移量。
- 初始化(Initializing)
- 执行由编译器生成的初始化方法clinit,是类加载的最后一步。完成静态变量赋值和静态代码块的执行,如果类有父类先初始化父类。
# 代码示例
public class ClassLoadingExample {
public static void main(String[] args) {
// 步骤1:加载
MyClass myClass = new MyClass();
// 步骤3:初始化
System.out.println(MyClass.staticField);
// 输出:Initialized static field
// 使用类中的方法
myClass.printMessage(); // 输出:Initialized static method
}
}
class MyClass {
// 步骤2:连接-准备
public static String staticField = "Initialized static field";
// 步骤3:初始化
static {
System.out.println("Initialized static method");
}
public void printMessage() {
System.out.println("Instance method called");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 知识图解
- JVM的组成-类加载器

- 类的生命周期

# 知识扩展
- 扩展:
- 类加载器ClassLoader:
- 启动类加载器(Bootstrap ClassLoader):是最顶层的加载类,JVM启动时加载核心类库(rt.jar,resources.jar,charset.jar等jar包和类,被参数-Xbootclasspath指定路径下的类)。是由C/C++编写的,不能修改。
- 扩展类加载器(Extension ClassLoader):是启动类加载器的子类,加载扩展目录(lib/ext)中的类库。
- 应用程序类加载器(Application ClassLoader):是扩展类加载器的子类,加载当前应用类路径下的所有jar包和类。
- 面试官可能追问:
Q1:类的生命周期是怎么样的?
- 类的生命周期包括加载、验证、准备、解析、初始化、使用和卸载7个阶段。
Q2:准备阶段分配的变量包含实例变量吗?
- 不包含。实例变量会在对象实例化时和对象一起被分配在Java堆中,类加载则是发生在所有实例化操作之前。
Q3:解析阶段一定在准备阶段之后,初始化之前吗?
- 不一定。解析阶段在JVM中是按需延迟解析,可以在初始化之前解析,也可以在首次使用该符号引用时再解析,调用方法时才解析方法的符号引用,这样可以避免不必要的解析开销。
Q4:哪些场景会触发类的初始化?
- 类的初始化会在以下场景触发:
- 当虚拟机启动时,初始化用户指定的主类,也就是含main方法的类。
- 有反射调用时会触发类的初始化。
- 当访问类的静态变量或调用静态方法时,会触发类的初始化。
- 当创建类的实例时,会触发类的初始化。
- 初始化子类时,会触发父类的初始化。
- 类的初始化会在以下场景触发:
Q5:类加载后会被卸载吗?什么时候会卸载?
- 当类的所有实例都被回收,加载该类的类加载器被回收以及该类的Class对象没有任何引用,无法通过反射等方式访问时JVM会卸载该类。
- 启动类加载器加载的核心类不会被卸载,因为该类加载器无法被回收。
Q6:类的卸载和实例的垃圾回收有什么区别?
- 类的卸载是回收方法区中类的元数据,比如字段和方法信息,类的生命周期结束;实例的垃圾回收是回收堆中对象的内存,是对象生命周期的结束。
← JVM常见参数? 介绍一下双亲委派机制? →
评论
验证登录状态...