类加载流程
类从被加载到虚拟机到从虚拟机卸载,经过了以下几个阶段:加载、验证、准备、解析、初始化、使用和卸载。
加载
类加载的时机包括以下几种情况:
- 遇到new、getstatic、putstatic或则invokestatic这四条指令,如果类没有被初始化,那么需要进行初始化。生成这4条指令主要是以下几个场景:使用new关键字实例化对象,get或者set类的静态字段(final修饰的除外),调用类的静态方法。
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发对其初始化;
- 在初始化一个类的时候,如果发现父类没有初始化,则需要先初始化父类;
- 当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先初始化这个主类。
在类加载阶段,虚拟机需要完成下面三件事:
- 通过一个类的全限定名来获取此类的二进制字节流;
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
- 在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口;
验证
验证主要包括文件格式、元数据验证和字节码验证。准备
准备阶段是为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区进行分配。这里的初始值指的是零值,比如public static int value = 1,在准备阶段value初始化为0,value = 1是在初始化阶段才会被执行。解析
初始化
类加载器
JVM在下面几种不同的层面使用不同的类加载器
- Bootstrap
classLoader(启动类加载器):是其他类加载器的父类,它用于加载Java核心库,并且是唯一一个用本地代码编写的类加载器。 - Extension ClassLoader(扩展类加载器):是bootstrap
class loader加载器的子类,用于加载扩展库。 - Application ClassLoader(应用程序类加载器):是extension
class loader加载器的子类,用于加载在classpath中的应用程序的类文件。 - User
classLoader(用户定义的类加载器):是系统类加载器或其他用户定义的类加载器的子类。
下图中的类加载器之间的这种层次关系,就称为类加载器的双亲委派模型。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器应当有自己的父类加载器。双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求时,子类加载器才会尝试自己去加载。
实现双亲委派的代码都集中在java.lang.ClassLoader的loadClass()方法当中,首先检查类是否已经被加载过,若没有加载则调用父加载器的loadClass方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载器失败,则在抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。
- 什么是类加载器的双亲委派模型,它解决了什么问题
类加载器的这种层次结构就是类的双亲委派模型,双亲委派模型解决了类被加载多次的问题。
在哪些场景会使用到双亲委派模型
何时需要自己定义类加载器