Jvm的类的生命周期与双亲委派机制
Jvm 之类的生命周期与双亲委派机制
一、在JVM(Java Virtual Machine)中类的生命周期包含以下 5 个阶段(有时也被称为7个阶段):
1、加载
加载阶段第一步是将==类加载器==根据类的全限定名通过不同渠道以二进制流的方式获取字节码信息。在此之后,jvm会将字节码信息保存到内存的方法区(InstanceKlass)中具体包括基本信息:如主次版本号,本类与父类索引,接口字段方法属性计数等信息。常量池:例如字面量(字符串hello、基本类型常量final int a=10、和声明为final的常量)和符号引用(类和接口的全限定名java/lang/Object, 字段的名称和描述符 name:Ljava/lang/String;,方法的名称和描述符如main([Ljava/lang/String;)V)
字段和方法、属性、虚方法表(用于实现多态)等信息。同时jvm还会再堆中生成一份与方法区帐数据类似的java.lang.Class对象(作用是在Java代码中获取类的信息以及存储静态字段数据)与方法区相关联。
目标:将
.class文件读取到内存。过程:
- 通过类加载器(
ClassLoader)查找类的二进制数据(如文件、网络、JAR包等)。 - 生成一个代表该类的
java.lang.Class对象。
- 通过类加载器(
2、链接
链接阶段又分为三个阶段分别是验证 、准备与解析阶段,我们分别来说明
首先是 验证 阶段:
主要包含四部分:
1.文件格式验证:如0xcafebabe的文件格式开头
2.元信息验证:例如类必须有父类
3.程序执行指令的语义验证:如方法执行到一半强行转到其他方法中
4.符号引用验证:如是否访问了其他类中的private的方法等
之后是 准备 阶段:
为静态变量分配内存并设初始值(jdk8之后)如
1 | public class Test(){ |
会在准备阶段给value变量value赋值为0(初始化阶段才会赋值)
但是若此静态变量被final关键字修饰,则直接会在准备阶段赋值即value2=1。
最后是 解析 阶段:
解析阶段主要是将常量池中的符号引用替换为直接引用。直接引用不再使用编号而是直接使用内存中的地址进行具体的数据访问。
3、初始化
在此阶段会执行静态代码块中的代码,并为静态变量赋值。
初始化阶段会执行字节码文件中clinit部分的字节码指令。
1 | public class Test2(){ |
静态代码块会影响静态变量赋值的顺序,如上代码最终value3=2,但若上述代码静态代码块和静态变量顺序调换则初始化阶段会将该变量初始化设值为1。
以下几种是导致类会被初始化的方式:
1.访问一个类的静态变量或者静态方法(变量被final修饰且等号右边为常量不会初始化)
2.调用Class.forName(String className)
3.new一个该类的对象时
4.执行Main方法的当前类
以下是几种不会被初始化的方式:
1.无静态代码块且无静态变量赋值语句
2.有静态变量声明但是没有赋值语句
3.静态变量使用final定义的,这类变量会在链接的准备阶段赋初值
需要注意:直接访问父类的静态变量,不会触发子类的初始化
子类的初始化clinit调用志气,会优先调用父类的clinit初始化方法
数组的创建不会导致数组中元素的类进行初始化
4、使用
类初始化完成之后,就可以在程序中使用这个类了。可以创建类的实例、调用类的方法、访问类的字段等。
5、卸载
当类不再被使用,并且满足一定条件时,JVM 会将类卸载,即回收类所占用的内存空间。类被卸载需要满足以下三个条件:
- 该类的所有实例都已经被回收。
- 加载该类的
ClassLoader已经被回收。 - 该类对应的
java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
二、双亲委派机制
首先要提到类加载器(ClassLoader):是jvm提供给应用程序去实现获取类和接口字节码数据的技术。
文件会通过类加载器二进制流的输入方式读取数据之后便可通过jvm调用本地接口(Java Native interface,允许Java调用其他语言编写的方法,主要调用jvm中的方法,这些方法使用C++编写)
jdk8及之前,类加载器分为两类,一类是Java代码中实现的,一类是Jvm底层源码实现的(如HotSpot使用C++),底层实现的如启动类加载器(Bootstrap)。Java中的如扩展类加载器(Extension)和应用程序类加载器(Application)。
双亲委派机制:当一个类加载器收到加载类的任务时,会自底向上查找是否加载过,再由顶向下进行加载
当一个类收到了加载请求时,它是不会先自己去尝试加载的,而是委派给父类去完成。只有当父类加载器都反馈自己无法完成这个请求(也就是父类加载器都没有找到加载所需的 Class)时,子类加载器才会自行尝试加载。

