JVM的类加载机制和加载过程

时间:2021-07-01 15:29:31

一、虚拟机的类加载机制

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

    与那些在编译器需要进行连接工作的语言不同,在Java语言中,类型的加载和连接过程都是在程序运行期间完成的,这样会在类加载时稍微增加一些性能开销,但是却能为Java应用程序提供高度的灵活性,Java中可以天生动态扩展的语言特性就是依赖运行期动态加载和动态连接这个特点实现的。

二、类加载的时机

    类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。其中验证、准备和解析三个部分统称为连接,这七个阶段的发生顺序为:

JVM的类加载机制和加载过程

    其中加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的开始,而解析阶段不一定,它在某些情况可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定。对于初始化阶段,虚拟机规范则是严格规定了有且只有四种情况必须立即对类进行初始化:

    1、遇到new、getstatic、putstatic或invokestatic这四条字节码指令时,如果类没有进行初始化,则需要先出发初始化;

    2、使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先出发初始化;

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

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

三、类的加载过程

    1、加载

    加载阶段是类加载过程中的一个阶段,在加载阶段,虚拟机要完成三件事情:

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

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

    1.3、在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口

    2、验证

    验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全,校验过程包含文件格式验证、元数据验证、字节码验证和符号引用验证。

    3、准备

    准备阶段是正式为类变量分配内存并设置类变量初始值的阶段这些内存都将在方法区中进行分配。这个时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配到Java堆中;这里的初始值通常情况下是数据类型的零值。

    4、解析

    解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,符号引用是指以一组符号来描述所引用的目标,直接引用是指直接指向目标的指针、相对偏移量或者是能间接定位到目标的句柄。

    5、初始化

    初始化阶段是类加载过程中的最后一步,这个阶段才真正开始执行类中定义的Java程序代码(或者说是字节码)。

四、类加载器

    从虚拟机的角度来说,类加载器分为两种:一种是启动类加载器,这是使用c++语言实现的,是虚拟机自身的一部分;另外一种就是所有其他的类加载器,这些类加载器是由Java语言实现的,独立于虚拟机外部,并且全部继承自抽象类java.lang.ClassLoader。但对于Java开发人员来讲,绝大部分Java程序都会使用到以下三种系统提供的类加载器:

    1、启动类加载器(Bootsrap ClassLoader):这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识别的类库加载到虚拟机内存中,启动类加载器无法被Java程序直接引用。

    2、扩展类加载器(Extension ClassLoader):它负责加载<JAVA_HOME>\lib\ext目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库,开发者可以直接使用扩展类加载器

    3、应用类加载器(Application ClassLoader):它负责加载用户类路径(ClassPath)上所指定的类库,开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

五、类加载器的双亲委派模型

JVM的类加载机制和加载过程

    双亲委派模型除了要求顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,双亲委派模型的工作过程是:如果一个类加载器收到了类加载请求,它首先自己不会尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去加载。