深入了解java虚拟机(JVM) 第十一章 类的加载

时间:2022-12-27 20:40:07

一、类加载机制概述

  虚拟机把描述类的数据从class文件加载到内存并对数据进行效验,解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。

二、类加载的机制

  类加载的过程一共是5个步骤:加载、连接、初始、使用、卸载,其中连接过程中又会进行验证、准备、解析的工作,具体如下:

    深入了解java虚拟机(JVM) 第十一章 类的加载

 

  需要注意的是:

  1.类的加载策略采用的是懒加载。

  2.在类加载开始后,才会进行连接。

  3.只有以下情况才会初始化类:

    a.new实例化的类会对该类进行初始化。

    b.读取或设置类静态字段的时候(但被final修饰的字段,在编译器时就被放入常量池的静态字段除外static final)。

    c.调用类静态方法的时候。

    e.使用反射对类进行反射调用的时候。

    f.当一个类初始化时,要先对其父类进行初始化。

    g.主类或启动类(main)。

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

三、加载

  1.加载的过程

    1)通过一个类的全限定名类获取定义此类的二进制类。

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

    3)在内存中生成一个代表这个类的class对象,作为这个类的各种数据的访问入口。

   2.加载源

    由于类的加载本质就是加载二进制流,这个二进制流的来源没有一个固定的规则,所以它可以来自多个地方:

    1)文件,如:class文件,jar包

    2)网络

    3)计算机生成一个二进制流,如:网络代理($proxy),反射包

    4)由其他文件生成,如:JSP

    5)数据库

 四、连接

  1.验证

    验证是连接的第一步,这一阶段的目的是为了确保class文件的字节流中包含的信息符合当前虚拟机的要求,而且不会危害虚拟机自身的安全。

     验证的方式主要有:

      1)文件格式验证;

      2)元数据验证;

      3)字节码验证;

      4)符合引用验证。

  2.准备

    准备阶段正式为类变量分配内存并设置变量的初始值。这些变量使用的内存都将在方法区中进行分配。

    需要注意的是:这里的初始值并非我们指定的值,而是数据类型的默认值,如:int类型的默认值为0;float类型的默认值为0.0

    但是如果被final修饰,那么这个过程中,常量值会被一同指定。

  3.解析

    解析阶段是虚拟机将常量池中的符号引用替换为直接引用的过程,主要分为以下几类:

      1)类或者接口解析

      2)字段解析

      3)类方法解析

      4)接口方法解析

五、初始化

  初始化是类加载的最后一步,简单的说,类的初始化就是执行<clinit>方法的过程,类的初始化主要以下几种情况:

  1.子类的<clinit>在执行之前,虚拟机保证父类的<clinit>方法先执行完毕。

  2.接口中有变量要赋值,也会生成<clinit>,但不需要先执行父接口的<clinit>方法,只有父接口中定义的变量使用时才会初始化

  3.如果多个线程同时初始化一个类,那么只有一个线程会执行这个类的<clinit>方法,其他线程等待执行完毕。如果方法执行时间过长,那么就会造成多个线程阻塞。由此我们就可以知道线程阻塞可能会出现的原因:线程执行类初始化时死掉了。

  4.<clinit>方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块中的语句合并产生的,编译器收集的顺序是有语句在源文件中出现的顺序决定的。静态语句块中只能访问定义在静态语句块之前的变量,定义在它之后的变量,在前面的语句中赋值,但是不能访问。