JVM知识点-双亲委派机制
注:更多文章,可以进入我的博客:筱白博客
机制流程:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把通过递归请求的方式委托给父类加载器去完成,依次向上,直到启动类加载器,因此所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父类加载器在它的搜索范围中没有找到所需的类时,子加载器才会尝试自己去加载该类。
JVM中的类加载器
①、启动类加载器(Bootstrap ClassLoader)
负责将存放在 <JAVA_HOME>/lib 目录中的,或者被 -Xbootclasspath 参数所指定的路径中的类库加载到内存中。
JDK 中的源码类大都是由启动类加载器加载,比如 java.lang.String,java.util.List等,需要注意的是,启动类 main Class 也是由启动类加载器加载。
②、扩展类加载器(Extension ClassLoader)
负责加载<JAVA_HOME>/lib/ext 目录中的,或者被java.ext.dirs系统变量所指定的路径中的所有类库。
开发者可以直接使用扩展类加载器。
③、应用程序类加载器(Application ClassLoader)
负责加载用户类路径ClassPath上所指定的类库。如果没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
通常项目中自定义的类,都会由应用程序类加载器加载。
④、自定义类加载器(User ClassLoader)
这是由用户自己定义的类加载器,一般情况下我们不会自定义类加载器,但有些特殊情况,比如JDBC能连接各种不同的数据库就是自定义类加载器来实现的。
为什么需要双亲委派机制
优点: 1. 避免类的重复加载 2. 避免Java的核心API被篡改
假设没有双亲委派模型,试想一个场景:
黑客自定义一个java.lang.String类,该String类具有系统的String类一样的功能,只是在某个函数稍作修改。比如equals函数,这个函数经常使用,如果在这这个函数中,黑客加入一些“病毒代码”。并且通过自定义类加载器加入到JVM中。此时,如果没有双亲委派模型,那么JVM就可能误以为黑客自定义的java.lang.String类是系统的String类,导致“病毒代码”被执行。
而有了双亲委派模型,黑客自定义的java.lang.String类永远都不会被加载进内存。因为首先是最顶端的启动类加载器就会加载系统的java.lang.String类,最终自定义的类加载器无法加载java.lang.String类。
或许你会想,我在自定义的类加载器里面强制加载自定义的java.lang.String类,不去通过调用父加载器不就好了吗?确实,这样是可行。但是,在JVM中,判断一个对象是否是某个类型时,如果该对象的实际类型与待比较的类型的类加载器不同,那么会返回false。
举个简单例子:
ClassLoader1、ClassLoader2都加载java.lang.String类,对应Class1、Class2对象。那么Class1对象不属于ClassLoad2对象加载的java.lang.String类型。