jdk的类加载机制
类加载的过程
类加载主要分为:
加载 -> 验证 -> 准备 -> 解析 -> 初始化
- 加载
加载的主要作用是将外部的 .class 文件,加载到 Java 的方法区内
- 验证
验证class文件,是否符合jvm虚拟机的规范
- 准备
类变量分配内存,并将其初始化为默认值
类变量在准备阶段就进行了初始化,但是在方法内部定义的变量不会在准备阶段进行初始化,因此ide提示局部变量未进行
- 解析
解析是将符号引用替换为直接引用的过程。符号引用是一种定义,可以是任何字面上的含义,而直接引用就是直接指向目标的指针、相对偏移量。主要分为以下几个步骤: - 类或接口的解析
- 类方法解析
- 接口方法解析
- 字段解析
几个经常发生的异常,就与这个阶段有关
- java.lang.NoSuchFieldError 根据继承关系从下往上,找不到相关字段时的报错
- java.lang.IllegalAccessError 字段或者方法,访问权限不具备时的错误
- java.lang.NoSuchMethodError 找不到相关方法时的错误
解析过程保证了相互引用的完整性,把继承与组合推进到运行时
- 初始化
先来看一端代码
class A {
static int a = 0;
static {
a = 1;
b = 1;
b = b + 1;
}
static int b = 0;
public static void main(String[] args) {
System.out.println(a);
System.out.println(b);
}
}
b = b + 1;由于b + 1中b中是变量,在静态代码块中无法引用
-
static 语句块,只能访问到定义在 static 语句块之前的变量
-
JVM 会保证在子类的初始化方法执行之前,父类的初始化方法已经执行完毕
类加载器
类加载器所做的事情就将class文件进行解析,加载到jvm的方法区;主要分为Bootstrap ClassLoader、Extention ClassLoader、App ClassLoader、Custom ClassLoader
-
Bootstrap ClassLoader
用于加载加载核心类库也就是 rt.jar、resources.jar、charsets.jar等 -
Extention ClassLoader
扩展类加载器,主要用于加载lib/ext目录下的jar包和.class文件 -
App ClassLoader
这是我们写的 Java 类的默认加载器,有时候也叫作 System ClassLoader。一般用来加载 classpath 下的其他所有 jar 包和 .class 文件,我们写的代码,会首先尝试使用这个类加载器进行加载 -
Custom ClassLoader
自定义加载器,支持一些个性化的扩展功能。
双亲委派加载机制
双亲委派机制的意思是除了顶层的启动类加载器以外,其余的类加载器,在加载之前,都会委派给它的父加载器进行加载。这样一层层向上传递,直到祖先们都无法胜任,它才会真正的加载。
ps:这样做的目的主要是解决同一个jvm中只能加载同一个类一次
自定义加载器
- tomcat的类加载机制
WebAppClassLoader的类加载器优先加载。等它加载不到的时候,再交给上层的 ClassLoader 进行加载。这个加载器用来隔绝不同应用的 .class 文件,比如你的两个应用,可能会依赖同一个第三方的不同版本,它们是相互没有影响的。
- SPI机制
SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制,主要使用 java.util.ServiceLoader 类进行动态装载。
spi机制的实现是基于让线程的上下文类加载器去加载ServiceLoader,由于当前线程上下文类加载器是基于App ClassLoader的,因此并不违反双亲加载机制。