浅谈类加载和双亲委派机制
以下是看了一个老师的教程,做一下笔记,方便复习使用
1、类加载过程
首先类加载到使用的过程会涉及到以下几个步骤:
加载 ---> 验证 ---> 准备 ---> 解析---> 初始化 ---> 使用 ---> 卸载
加载:在硬盘上查找并通过IO读入字节码文件,使用到类时才会加载,例如调用类的main()方法,new对象等等
验证:校验字节码文件的正确性
准备:给类的静态变量分配内存,并赋予默认值
解析:将符号引用替换为直接引用,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的 静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用,下节课会讲到动态链接
初始化:对类的静态变量初始化为指定的值,执行静态代码块
2、类加载器
上面的类加载过程主要是通过类加载器来实现的,Java里有如下几种类加载器
启动类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如 rt.jar、charsets.jar等
扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中 的JAR类包
应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载你自己写 的那些类
自定义加载器:负责加载用户自定义路径下的类包
看一个类加载器示例:
public class Demo {
public static void main(String[] args){
System.out.println(String.class.getClassLoader());
System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
System.out.println(Demo.class.getClassLoader().getClass().getName());
System.out.println(ClassLoader.getSystemClassLoader().getClass().getName());
}
}
运行结果:
null
sun.misc.Launcher$ExtClassLoader
sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader
3、双亲委派机制
1. 首先,检查一下指定名称的类是否已经加载过,如果加载过了,就不需要再加载,直接返回。
2. 如果此类没有加载过,那么,再判断一下是否有父加载器;如果有父加载器,则由父加载器加载(即调用parent.loadClass(name, false);).或者是调用bootstrap类加载器来加载。
3. 如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的findClass方法来完成类加载。
说的简单点的话就是 :先找父亲加载,不行再由儿子自己加载
接下来看一下源码及自己添加的注释:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
//name是类名 , 判断当前的类是否被加载
Class<?> c = findLoadedClass(name);
//c==null,即未加载
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
//父类不为空,反复的去找父类,依次去找应用程序类加载器-->扩展类加载器-->启动类加载器
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();
//最终最终还是为空的话,就去自己去执行,即findClass
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
4、为什么要设计双亲委派机制?
沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改
避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性

浙公网安备 33010602011771号