[jvm]类加载机制

一、什么是JVM的类加载机制

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

二、什么时候进行类加载

  1. 类的生命周期

    类从被加载到虚拟机内存中开始,到卸载出内存为止。它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个部分统称为连接(Linking)
    在这里插入图片描述
    加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的。但是解析阶段却不一定,它为了支持java语言的运行时绑定,可以在初始化阶段之后再开始。

  2. 类加载(初始化)的时机
    什么时候开始类加载机制的第一阶段:加载?这并没有做强制的要求,但是对于类的初始化,JVM规定了有且仅有以下五种情况必须对类进行初始化(加载、验证、准备自然需要再次之前完成):

    1. 使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。对应的字节码指令是new、getstatic、putstatic或invokestatic,如果类没有进行过初始化,则需要先触发其初始化。
    2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
    3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
    4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
    5. 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
      注:除了以上五种情况(主动引用)类必须被初始化之外,其他引用类的方式(被动引用都不会触发初始化):
      被动引用举例
    /**
    *被动使用类字段演示一:
    *通过子类引用父类的静态字段,不会导致子类初始化
    **/
    public class SuperClass{
    static{
    System.out.println("SuperClass init!");
    }
    public static int value=123;
    }
    public class SubClass extends SuperClass{
    static{
    System.out.println("SubClass init!");
    }}
    /**
    *非主动使用类字段演示
    **/
    public class NotInitialization{
    public static void main(String[]args){
    System.out.println(SubClass.value);
    }
    }
    

三、类的加载过程

类加载过程

四、类加载器

  1. 类与类加载器的关系
    任意的一个类和加载它的类加载器组合在一起才能确立在虚拟机中的唯一性。
    简单的来说就是比较两个类是否“相等”,前提条件是这两个类是通过同一个类加载器加载的,否则的话,即使这两个类是来自同一个class文件,他们也不会相等。

  2. 双亲委派模型
    从java开发人员的角度,大多数情况下都会使用到系统提供的三类加载器:

    1. 启动类加载器(Bootstrap ClassLoader):负责将存放在<JAVA_HOME>lib目录中的,或者被-bootclasspath参数所指定的路径中的,并且是虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器由c++语言编写,无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器,那直接使用null代替即可。
    2. 扩展类加载器(Extension ClassLoader):这个加载器由sun.misc.Launcher
      $ExtClassLoader实现,它负责加载<JAVA_HOME>libext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器。
    3. 应用程序类加载器(Application ClassLoader):这个类加载器由sun.misc.Launcher $App-ClassLoader实现。由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

    注:除此之外,还可以自定义类加载器,他们的关系如下图所示:
    双亲委派模型
    双亲委派模型的工作过程:
    如果一个类加载器收到了类加载的请求,它首先不会自己
    去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
    优点:
    Java类随着它的类加载器一起具备了一种带有优先级的层次关系。

原文地址:https://www.cnblogs.com/ytuan996/p/10573612.html