深入理解Java (四)类加载过程

类加载过程

类的生命周期

    类的生命周期包括7个部分:加载–验证–准备–解析–初始化–使用–卸载
   其中验证–准备–解析 称为连接阶段,其他阶段是顺序发生的,而解析可以与这些阶段交叉进行,因为Java支持动态绑定(晚期绑定),需要运行时才能确定具体类型。

类的初始化触发

   类的加载机制没有明确的触发条件,但有5种情况下必须对类进行初始化,那么 加载–验证–准备 就必须在此之前完成了。
   1.new、getstatic、putstatic、invokestatic这4个字节码指令时对类进行初始化(即:实例化对象、读写静态对象、调用静态方法时,对类进行初始化)
   2.使用反射机制对类进行调用时,进行类的初始化。
   3.初始化一个类,其父类没有初始化时,先初始化其父类
   4、虚拟机启动时,初始化一个执行主类
   5、使用JDK的动态语言支持时,如果MethodHandle实例的解析结果为REF_getstatic、REF_putstatic、REF_invokestatic的方法句柄(即:读写静态对象或者调用静态方法),则初始化该句柄对应类;
   一般,以上5种情况最常见的是前三种: 实例化对象、读写静态对象、调用静态方法、反射机制调用类、调用子类触发父类初始化

类的加载过程

    从用户角度来说,类(对象)的生命周期只需笼统理解为“加载——使用——卸载”即可,无需太过深入。所以,这里的类加载过程就是我们说的 加载——验证——准备——解析——初始化 这五个使用前的阶段
   1、加载:加载阶段,虚拟机需要完成三件事:通过类名字获取类的二进制字节流——将字节流的内容转存到方法区——在内存中生成一个Class对象作为该类方法区数据的访问入口。
   其中,第一步:通过类名获取类的二进制字节流是通过类加载器来完成的。其加载过程使用“双亲委派模型”:

   启动类加载器:加载系统环境变量下JAVA_HOME/lib目录下的类库。

   扩展类加载器:加载JAVA_HOME/lib/ext目录下的类库。

   应用程序类加载器(系统类加载器):加载用户类路径Class_Path指定的类库。(我们可以在使用第三方插件时,把jar包添加到ClassPath后就是使用了这个加载器)

   自定义加载器:如果需要自定义加载时的规则(比如:指定类的字节流来源、动态加载时性能优化等),可以自己实现类加载器。

   双亲委派模型是指:当一个类加载器收到类加载请求时,不会直接加载这个类,而是把这个加载请求委派给自己父加载器去完成。如果父加载器无法加载时,子加载器才会去尝试加载。

   采用双亲委派模型的原因:避免同一个类被多个类加载器重复加载。
   2:验证 确保class文件的二进制字节流中包含的信息符号虚拟机要求,包括:文件格式验证、元数据验证(数据语义分析)、字节码验证(数据流语义合法性)、符号引用验证(符号引用的匹配性校验,确保解析能正确执行)
   3:准备 为类变量(静态变量)在方法区分配内存,并设置零值。注意:这里是类变量,不是实例变量,实例变量是对象分配到堆内存时根据运行时动态生成的。
   4:解析 把常量池中的符号引用解析为直接引用:根据符号引用所作的描述,在内存中找到符合描述的目标并把目标指针指针返回。
   5:初始化 真正开始执行Java程序代码,该步执行方法根据代码赋值语句,对 类变量和其他资源 进行初始化赋值。
   方法:编译器自动收集类中所有 类变量的赋值语句和静态语句合并而成,收集的顺序是在程序代码出现的顺序。所以,静态语句中只能访问到定义在静态语句块之前的变量,在其之后的变量可以赋值(相当于新建并赋值了)但不可以访问(因为还没出现)。
   分析向上转型的例子时的程序代码的运行顺序:父类静态内容——子类静态内容——父类构造——子类构造——子类方法 。
   在经历了上面5步“加载”阶段后,才真正地可以使用class对象或者使用实例对象。使用过后,不再需要用到该类的class对象或者实例对象时,就会把类卸载掉(发生在方法区的垃圾回收:无用类的卸载)。

   对象是由类创建出来的,所以对象的生命周期就是包含在类的生命周期中:

   类加载(5步)——创建类的实例对象——使用对象——对象回收——类卸载

你可能感兴趣的:(java)