双亲委派模型
双亲只是"parent"的直译,全称parent-delegation model(虽然我还是无法理解parent为什么翻译成双亲??)
作用:
对于任何一个类,都需要这个类本身和加载它的类加载器一同来确定其在虚拟机的唯一性,每一个类加载器都拥有一个独立的类名称空间。因此使用双亲委派模型来组织类加载器之间的关系,主要体现两个好处:
1.类伴随它的类加载器一起具备了一种带有优先级的层次关系,确保了在各种加载环境的加载顺序。
2.保证了运行的安全性,防止不可信类扮演可信任的类
例:类java.lang.Object
,它由启动类加载器加载,双亲委派模型保证任何类加载器收到的都是对java.lang.Object
的加载请求,最终通过这个模型把它委托给处于Bootstrap(最顶端的启动类加载器)进行加载,保证了Object类在程序的各个类加载器环境中都是同一个类。相反:如果没有双亲委派模型,由各个类加载器自行去加载的话,如果用户自己编写了一个成为java.lang.Object
的类,并用自定义的类加载器去加载,那么系统中会出现多个不同的Object类,Java类型体系中最基础的行为也就无法保证,应用程序也将会变得一片混乱。
类在JVM中加载的规则,任何一个类只需要在JVM中加载一次,不需要重复加载,对象可以有n个,但是对象的模板类只需要有一个(只需要一个模板即可以加载无数对象),可以节省内存的空间。
加载一个类的时候,首先从自定义加载器开始查找,看它有没有加载过这个类,如果加载过,则直接返回,如果没有加载,怎么办?就委派给AppClassLoader去加载。
开始查找AppClassLoader有没有加载过这个类,如果加载过,则直接返回,否则委派给ExtClassLoader去加载,再重复上述过程直到BootstrapClassLoader(顶层的启动类记载器),如果加载过,直接返回,如果此时还是没加载过,就会逆向加载,将整个过程反过来
BootstrapClassLoader–>ExtClassLoader–>AppClassLoader–>自定义ClassLoader
完整过程走下来如果还是没有加载到,就会抛出ClassNotFoundException。
为什么需要双亲委派模型呢?
如果某个程序员自定义了一个java.lang.String类,它的功能和系统自带的String类一致,但是如果它在某个函数稍加改动,通过自定义类加载器中加载到java虚拟机中,如果没有双亲委派机制,系统很有可能会把这个自定义String类识别成系统中自带的String类,程序员就可以利用这个自定义类恶意添加代码威胁系统的安全。而双亲委派模型就保证了类和加载这个类的类加载器在jvm中的唯一性。
loadClass加载类方法的源码
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
//上锁,避免多线程导致类重复加载
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
//c==null,表示没有加载过
if (c == null) {
long t0 = System.nanoTime();
try {
//判断父加载器是否为null,有无父加载器
if (parent != null) {
//委托父类进行加载
c = parent.loadClass(name, false);
} else {
//如果父加载器==null,说明到了最顶层的启动类加载器,Bootstrap加载该类
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//如果还是没有加载,调用Bootstrap的findClass加载
//依次向下发展
//如果还是找不到则抛出ClassNotFoundException
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
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
参考链接:https://blog.youkuaiyun.com//qq_39595349/article/details/103978323