0%

类加载过程与双亲委派机制

类加载过程

  1. 加载】JVM把.class文件加载到方法区,生成构造函数、接口、静态变量(static)、常量(final)池、静态代码块

    并在堆中生成对应的Class对象

  2. 链接】将类的二进制代码合并到JVM的运行状态之中
    • 验证
    确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备
    正式为static变量分配内存并设置默认值(和Bean实例化很像),具体赋值在初始化阶段完成
    • 解析
    虚拟机常量池内的符号引用替换为直接引用(地址引用)的过程

  3. 初始化】如果发现其父类还没有进行过初始化,则需要先初始化其父类

    ​ 顺序如下(其实很有规律)

    1、父类的静态变量
    2、父类的静态代码块
    3、子类的静态变量
    4、子类的静态代码块
    5、父类的非静态变量
    6、父类的非静态代码块
    7、父类的构造方法
    8、子类的非静态变量
    9、子类的非静态代码块
    10、子类的构造方法

.image-20220220153623885

双亲委派机制

自定义ClassLoader→AppClassLoader(自定义的类)→PlatformClassLoader(jre/lib/ext/*.jar)→BootClassLoader(jre/lib/rt.jar)

始终加载能找到的最上层的类(防止自定义String类等),这样保证了安全

为什么叫双亲委派?

名字翻译有问题

强行的解释是:自己定义的类要委派给ExtensionClassLoaderBootstrapClassLoader的“双亲”

Tomcat打破双亲委派机制

如何打破?

重写ClassLoader.loadClass方法

源码👇

.image-20220307155653078

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//Step1.调用findLoadedClass(String)检查该Class是否已被加载
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//Step2.若未加载,则调用父加载器的loadClass()方法
c = parent.loadClass(name, false);//**就是这步实现的双亲委派**
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}

if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);

// this is the defining class loader; record the stats
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//Step3.若resolve为true,则调用resolveClass()方法
resolveClass(c);
}
return c;
}
}

自定义的ClassLoader需要重写**loadClass和findClass**方法

Tomcat 遵守双亲委派模型,每个WebAppClassLoader加载自己目录下的class文件

.image-20220307164904734

为何打破?

  • 隔离:部署在同一个容器中的不同应用A和B,例如A用了Spring2.5,B用了Spring3.5,如果A和B使用的是同一个类加载器,那么Web应用就会因为jar包覆盖而无法启动

  • 性能:如果在一个Tomcat部署多个应用,他们都有相同的类库依赖,那么可以把相同的类库让CommonClassLoader进行加载