1、类加载过程
类的生命周期会经历 加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载 几个阶段,其中验证、准备、解析合并称作连接。类的加载过程会经历加载、连接、初始化三个阶段。
下面我们来分别总结一下每个阶段的工作
加载(loading)是将类对应的class文件读取到内存中,并为该类创建一个Class<T>对象的一个过程。类加载是通过类加载器完成的。
通过类的全限定名来获取定义此类的二进制字节流,将这个类字节流代表的静态存储结构转为方法区的运行时数据结构,在堆中生成一个Class<T>对象,作为访问方法区这些数据结构的入口。
连接(Linking)
- 验证(Verification)确保类或接口的二进制表示在结构上是正确的。
- 准备(Preparation)准备工作包括为类或接口创建静态字段,并将这些字段初始化为其默认值。
- 解析(Resolution)从运行时常量池中的符号引用动态确定具体值的过程。
初始化(Initializing)执行类或者接口的初始化方法。
为方便理解我们将类的加载过程用图形的方式展示出来:
2、类加载时机
上图已经总结有会对类进行初始化的节点:
- 使用new关键字创建类的实例。
- 访问类的静态变量、静态方法。
- 初始化类时,如果父类没有被初始化过,则先初始化父类。
- 使用java反射机制进行访问类的信息时,如果类没有被初始化,会进行该类的初始化动作。
- 虚拟机启动时,用户会先初始化要执行的主类(含有main)
- jdk 1.7后,如果java.lang.invoke.MethodHandle的实例最后对应的解析结果是 REF_getStatic、REF_putStatic、REF_invokeStatic方法句柄,并且这个方法所在类没有初始化,则先初始化。
3、类加载器
类加载器负责所有类的加载。类加载器可以分为启动类加载器、扩展类加载器、应用程序加载器、用户自定义加载器。
- 启动类加载器(BootstrapClassLoader):负责加载 $JAVA_HOMElib路径 及 -Xbootclasspath 参数指定的路径下的类。
- 扩展类加载器(ExtClassLoader):负责加载 $JAVA_HOMElib路径 及 java.ext.dir 系统变量()执行System.getProperty("java.ext.dir"获取查看)的路径下的类。
- 应用程序类加载器(AppClassLoader):负责加载classpath下的类。
- 用户自定义类加载器(UserDefinedClassLoader):负责加载用户指定类的类。
除启动类加载器(虚拟机本身的实现)外都会存在一个父加载器,父子关系如下图所示。
当类加载器接收到类的加载指令后并不会立刻进行类加载,而是询问父加载器是否可以进行加载,父加载器又会执行相同动作直至询问启动类加载器。
启动类加载器会确认需要加载的类是否为自己的加载范围内,如果不是则反馈子加载器可以进行加载,依次返回直至找到可以进行加载的类加载器。
这种类加载器之间的层次关系就是类加载器的双亲委派模型。
参考文献:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html