类的生命周期
生命周期:加载 -> 连接 -> 初始化 -> 使用 -> 卸载
加载阶段
加载阶段主要完成以下两个步骤:
-
加载类的字节码文件:
- 类加载器(Class Loader)负责从文件系统、网络或其他来源找到类的字节码文件(.class文件),并将其读入到JVM中。
- 类加载器通常按需加载,即第一次使用该类时才加载。
-
数据存储:
- 一旦字节码文件被读入JVM,它将被存储在方法区(Method Area)中。生成一个InstanceKlass对象,保存类的所有信息,里边还包含实现特定功能比如多态的信息。方法区是JVM内存中的一个逻辑区域,用于存储类结构信息,如字段、方法、构造函数等。
- 在这个阶段,JVM会在堆中为这个类创建一个对应的
java.lang.Class
对象,作为方法区中这个类的各种数据的访问入口。对于开发者来说,只需要访问堆中的Class对象而不需要访问方法区中所有信息。 这样Java虚拟机就能很好地控制开发者访问数据的范围。
连接阶段
验证
连接(Linking)阶段的第一个环节是验证,验证的主要目的是检测Java字节码文件是否遵守了《Java虚拟机规 范》中的约束。这个阶段一般不需要程序员参与。
主要包含如下四部分:
- 文件格式验证,比如文件是否以0xCAFEBABE开头,主次版本号是否满足当前Java虚拟机版本要求。
- 元信息验证,例如类必须有父类(super不能为空)。
- 验证程序执行指令的语义,比如方法内的指令执行到一半强行跳转到其他方法中去。
- 符号引用验证,例如是否访问了其他类中private的方法等。
准备
准备阶段是连接阶段的第二步,它为类变量分配内存,并设置默认初始值。这些变量所使用的内存都在方法区中进行分配。准备阶段需要完成以下工作:
- 分配内存:为类变量分配内存,这些变量不包括实例变量,实例变量会在对象实例化时随着对象一起分配在堆内存中。
- 设置默认值:为类变量设置默认初始值,例如int类型的变量默认值为0,对象的引用默认值为null。(final修饰的基本数据类型的静态变量,准备阶段直接会将代码中的值进行赋值。)
解析
解析阶段是连接阶段的第三步,它是将类、接口、字段和方法的符号引用(字节码文件中使用编号来访问常量池中的内容)转换为直接引用(内存地址)的过程。
初始化阶段
- 初始化阶段会执行静态代码块中的代码,并为静态变量赋值。执行流程与代码流程一致。
- 初始化阶段会执行字节码文件中clinit部分的字节码指令。
以下几种方式会导致类的初始化:
- 访问一个类的静态变量或者静态方法,注意变量是final修饰的并且等号右边是常量不会触发初始化。
- 调用Class.forName(String className)。
- new一个该类的对象时。
- 执行Main方法的当前类。
java8中添加-XX:+TraceClassLoading
参数可以打印出加载并初始化的类
java8以上版本可添加-Xlog:class+load=info
参数打印出加载并初始化的类