类的生命周期
类的生命周期描述了一个类加载、使用、卸载的整个过程。整体可以分为:
- 加载
- 连接,其中又分为验证、准备、解析三个子阶段
- 初始化
- 使用
- 卸载
1.加载阶段
-
加载阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息。
-
类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到方法区中,方法区中生成一个InstanceKlass对象,保存类的所有信息,里边还包含实现特定功能比如多态的信息。
-
Java虚拟机同时会在堆上生成与方法区中数据类似的java.lang.Class对象,作用是在Java代码中去获取类的信息以及存储静态字段的数据(JDK8及之后)。
生成这个对象的原因是为了安全考虑,堆区中对象存储的数据是比方法区少的,堆区只存放程序员可以用到的数据。
2.验证阶段
验证的主要目的是检测Java字节码文件是否遵守了《Java虚拟机规范》中的约束。这个阶段一般不需要程序员参与。主要包含如下四部分:
- 文件格式验证,比如文件是否以0xCAFEBABE开头,主次版本号是否满足当前Java虚拟机版本要求。
- 元信息验证,例如类必须有父类(Object)
- 验证程序执行指令的语义,比如方法内的指令执行中跳转到不正确的位置。
- 符号引用验证,例如是否访问了其他类中private的方法等
3.准备阶段
准备阶段为静态变量(static)分配内存并设置初值,每一种基本数据类型和引用数据类型都有其初值
4.解析阶段
解析阶段主要是将常量池中的符号引用替换为直接引用,符号引用就是在字节码文件中使用编号来访问常量池中的内容。
直接引用不在使用编号,而是使用内存中地址进行访问具体的数据。
5.初始化阶段
初始化阶段会执行字节码文件中clinit(class init 类的初始化)方法的字节码指令,包含了静态代码块中的代码,并为静态变量赋值。
6.使用阶段
在类的使用阶段,类可以被JVM中的其他部分直接或间接地引用。以下是关于直接引用和间接引用的详细说明:
- 直接引用:
当代码通过new关键字创建类的实例时,这就是一个直接引用。
访问类的静态变量或调用类的静态方法也是一种直接引用。
直接引用会触发类的初始化,如果类尚未被初始化的话。这意味着JVM会执行类的静态初始化代码块和静态变量的赋值语句。 - 间接引用:
间接引用通常发生在引用类的字段、方法或父类/接口时,但不是通过直接的代码操作。
例如,如果一个类是另一个类的超类或者实现了某个接口,那么对该超类或接口的引用就是间接引用。
间接引用通常不会触发类的初始化,除非涉及到反射或其他特殊操作。
类的使用阶段是类生命周期中最长的阶段,它涵盖了类在程序运行期间的所有活动,包括实例化对象、调用方法、访问静态成员等。
7.卸载阶段
当类不再被使用时,它可能会进入卸载阶段。以下是关于类卸载的详细说明:
- 卸载条件:
类的卸载是由JVM的垃圾回收器(GC)管理的。当类的所有实例都不可达(也就是说,没有任何引用指向这些实例)时,垃圾回收器可能会标记该类为可回收。
类的卸载还取决于JVM的实现和配置。某些JVM可能允许类在特定条件下被卸载,而其他JVM则可能不允许。 - 卸载过程:
当垃圾回收器确定一个类不再被使用时,它会从方法区中清除类的相关信息,包括类的字节码、静态变量、常量池等。
卸载类后,该类的java.lang.Class对象也会被回收,释放相关内存空间。 - 注意事项:
类的卸载不是频繁发生的事件,通常只在应用程序结束时或者在某些特定情况下(如OSGi等模块化系统中)才会发生。
某些类可能永远不会被卸载,例如那些被JVM自身或系统类加载器加载的类。