乐趣区

jvm类加载机制

Java 虚拟机将字节流转化为 Java 类的过程。这个过程可分为加载、链接以及初始化
三大步骤。
加载是指查找字节流,并且据此创建类的过程。加载需要借助类加载器,在 Java 虚拟机中,类加载
器使用了双亲委派模型,即接收到加载请求时,会先将请求转发给父类加载器。
链接,是指将创建成的类合并至 Java 虚拟机中,使之能够执行的过程。链接还分验证、准备和解析
三个阶段。其中,解析阶段为非必须的。
初始化,则是为标记为常量值的字段赋值,以及执行 < clinit > 方法的过程。类的初始化仅会被执
行一次,这个特性被用来实现单例的延迟初始化。
加载
加载,是指查找字节流,并且据此创建类的过程。前面提到,对于数组类来说,它并没有对应的字节流,而是由 Java 虚拟机直接生成的。对于其他的类来说,Java 虚拟机则需要借助类加载器来完成查找字节流的过程。
由类加载器 classLoadder 负责类装载,JDK 默认提供了如下几种 ClassLoader
Bootstrp loader
Bootstrp 加载器是用 C ++ 语言写的,它是在 Java 虚拟机启动后初始化的,它主要负责加载 %JAVA_HOME%/jre/lib,-Xbootclasspath 参数指定的路径以及 %JAVA_HOME%/jre/classes 中的类。
ExtClassLoader
Bootstrp loader 加载 ExtClassLoader, 并且将 ExtClassLoader 的父加载器设置为 Bootstrp loader.ExtClassLoader 是用 Java 写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader 主要加载 %JAVA_HOME%/jre/lib/ext,此路径下的所有 classes 目录以及 java.ext.dirs 系统变量指定的路径中类库。
AppClassLoader
Bootstrp loader 加载完 ExtClassLoader 后,就会加载 AppClassLoader, 并且将 AppClassLoader 的父加载器指定为 ExtClassLoader。AppClassLoader 也是用 Java 写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道 ClassLoader 中有个 getSystemClassLoader 方法, 此方法返回的正是 AppclassLoader.AppClassLoader 主要负责加载 classpath 所指定的位置的类或者是 jar 文档,它也是 Java 程序默认的类加载器。

java 采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其 Parent 使用其搜索路径帮忙载入,如果 Parent 找不到, 那么才由自己依照自己的搜索路径搜索类”,或者叫双亲委派模型。
链接
链接,是指将创建成的类合并至 Java 虚拟机中,使之能够执行的过程。它可分为验证、准备以及解析三个阶段。

验证阶段的目的,在于确保被加载类能够满足 Java 虚拟机的约束条件。
准备阶段的目的,则是为被加载类的静态字段分配内存。Java 代码中对静态字段的具体初始化,则

会在稍后的初始化阶段中进行。
在 class 文件被加载至 Java 虚拟机之前,这个类无法知道其他类及其方法、字段所对应的具体地址,甚至不知道自己方法、字段的地址。因此,每当需要引用这些成员时,Java 编译器会生成一个符号引用。在运行阶段,这个符号引用一般都能够无歧义地定位到具体目标上。
初始化
在 Java 代码中,如果要初始化一个静态字段,我们可以在声明时直接赋值,也可以在静态代码块中对其赋值。
类的初始化何时会被触发呢?JVM 规范枚举了下述多种触发情况:

当虚拟机启动时,初始化用户指定的主类;
当遇到用以新建目标类实例的 new 指令时,初始化 new 指令的目标类;
当遇到调用静态方法的指令时,初始化该静态方法所在的类;
当遇到访问静态字段的指令时,初始化该静态字段所在的类;
子类的初始化会触发父类的初始化;
如果一个接口定义了 default 方法,那么直接实现或者间接实现该接口的类的初始化,会触发

该接口的初始化;

使用反射 API 对某个类进行反射调用时,初始化这个类;
当初次调用 MethodHandle 实例时,初始化该 MethodHandle 指向的方法所在的类。

退出移动版