天天看点

类的初始化——生命周期

加载

(1.生成类对象)

通过一个类的全限定名来获取定义此类的二进制字节流。

将这个字节流所代表的类静态存储结构转化为方法区的运行时数据结构。

在堆中生成一个代表这个类的 java.lang.Class 对象(这样便可以通过该对象访问步骤2中方法区的那些数据结构)。

连接

(验证)

总结一下:验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证没有问题,那么可以考虑采用 -Xverify:none参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

准备:

(静态变量内存分配,方法区内初始化赋值,static和final修饰的值(全局常量)放入常量池

1、这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一块分配在Java堆中。

2、这里设置类变量的初始值是数据类型默认的零值

解析:

(1.常量池里面的值都是符号引用(就是瞎写,等到对象生成的时候再指向对象))

1.解析阶段是虚拟机将常量池内的符号引用(JVM 并不知道引入的其他对象在哪里,所以就用唯一符号来代替)转化为直接引用(内存地址)的过程。

初始化

(执行代码,执行static语句,和静态变量的显示赋值)

初始化是类加载过程的最后一个步骤,直到这一阶段虚拟机才真正开始执行类中编写的代码。

初始化就是执行一个class中的static语句和所有类变量的赋值操作(对应字节码就是clinit方法)。

使用

(主动引用就是类加载时机;被动引用:除了下述5种场景,其他所有类的方式都不会触发初始化,称为被动引用。)

使用阶段包括主动引用(初始化阶段所做的事情就是主动引用)和被动引用,需要注意的是:被动引用不会引起类的初始化。

卸载

一个类什么时候结束生命周期,取决于它的 Class 对象何时结束生命周期。需要注意的是:由 Java 虚拟机自带的类加载器所加载的类,在虚拟机的生命周期中,始终不会被卸载。

所以类在使用阶段完成后,如果满足下面这些情况,就会被卸载掉:

  • 加载该类的类加载器已经被回收了
  • jvm 堆中不存在该类的任何实例了
  • 该类对应的 Class 对象没有被引用了

总结:

(生成类对象)

(验证)

(静态变量内存分配,在方法区内初始化赋值,static和final修饰的值(全局常量)放入常量池)

(常量池里面的值都是符号引用(就是瞎写,等到对象生成的时候再指向对象))

(执行代码,执行static语句,和静态变量的显示赋值)

(主动引用就是类加载时机;((new对象)(反射)(子类准备生成对象,父类就要开始初始化了,main方法(当前类进入类加载)-》(准备生成对象了,在用构造器之前先把非静态的成员变量走一遍,最后构造器赋值))

被动引用:除了下述5种场景,其他所有类的方式都不会触发初始化,称为被动引用。)

(人话:main方法触发类加载,静态的先走一遍(所有触发了类加载的类都按顺序走一遍静态,先初始化再赋值),静态的走完了,执行main里面的语句,开始走构造器,在构造器之前再把类的成员变量初始化和赋值走完)之后等卸载

----------

主动引用

java类的初始化阶段,虚拟机规范严格规定了5种情况必须立即对类进行初始化。

1.遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。(new对象)

2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。(反射)

3.当初始化一个类时,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化。

(子类初始化之前父类一定先初始化)

4.当虚拟机启动时,用户需要制定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个类。

(main方法)

5.当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_geStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。