双亲委派机制
-
官方定义
-
双亲委派机制是 Java 虚拟机(JVM)类加载机制中的一种层次化的类加载策略。当一个类加载器(ClassLoader)收到类加载请求时,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器。只有当父类加载器无法完成加载任务(即它在自己的搜索范围内找不到对应的类)时,子加载器才会尝试自己去加载。这种机制就像一个孩子(子加载器)遇到问题先问父母(父加载器),只有父母解决不了才自己动手。
-
-
主要特点
-
层次化的加载顺序:类加载器之间形成了一种层次关系,如启动类加载器(Bootstrap ClassLoader)处于最顶层,然后是扩展类加载器(Extension ClassLoader),再是应用程序类加载器(Application ClassLoader)等。加载请求会按照这个层次结构自顶向下传递,保证了类加载的顺序性和系统性。
-
保证类的唯一性:通过双亲委派机制,同一个类只会被加载一次。因为在层次结构中,父类加载器加载过的类,子类加载器不会再重复加载。例如,如果启动类加载器已经加载了
java.lang.String
类,那么应用程序类加载器就不会再加载这个类,从而避免了类的重复加载和可能导致的类型冲突。
-
-
主要应用场景和用途
-
保证核心类库的安全性和一致性:在 Java 运行环境中,核心类库(如
java.lang
包中的类)是由启动类加载器加载的。通过双亲委派机制,这些核心类库能够优先加载,并且不会被其他恶意或错误的同名类所替代。例如,防止用户自定义的一个名为java.lang.String
的类被错误加载,从而保证了 Java 程序运行的基本安全和核心功能的一致性。 -
类加载的分工协作:不同层次的类加载器负责加载不同范围的类,实现了一种分工。启动类加载器负责加载 Java 的核心类库,扩展类加载器加载 Java 扩展库,应用程序类加载器加载应用程序自身的类。这种分工使得类加载过程更加有序,也便于管理和维护。
-
-
与主流技术的相似之处和区别
-
与自定义类加载策略的相似与区别:
-
相似之处:都是用于控制类的加载过程。自定义类加载策略可以根据特定的需求灵活地加载类,而双亲委派机制本身也是一种类加载策略。
-
区别:双亲委派机制是 Java 默认的、标准化的类加载方式,它具有固定的层次结构和加载顺序,主要目的是保证类加载的安全性和一致性。自定义类加载策略则可以打破这种常规的加载顺序,用于一些特殊的场景,如热部署(在不重启应用程序的情况下更新某些类)、加载加密的类文件等。但如果自定义类加载策略设计不当,可能会破坏类加载的安全性和正常秩序。
-
-
与其他编程语言的模块加载系统的相似与区别:
-
相似之处:与一些编程语言(如 Python 的模块加载系统)类似,都有一个用于管理和加载代码单元(类或模块)的机制,目的都是为了正确地组织和加载代码,以便程序能够正常运行。
-
区别:Java 的双亲委派机制具有更严格的层次结构和安全性保障。在 Python 中,模块加载相对更加灵活,没有像 Java 这样严格的双亲委派式的层次结构,它更侧重于模块的查找路径和命名空间的管理,虽然也有一定的顺序来查找模块,但不像 Java 双亲委派机制那样形成一个严格的、自顶向下的层次加载体系。
-
-
双亲委派机制--再理解
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载器加载这个类,最终都是委托给顶层的启动类
加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同样一个 Object 对象
启动类加载器,加载JAVA_HOME/lib;
扩展类加载器,加载JAVA_HOME/lib/ext目录中;
应用程序类加载器,负责用户路径上的类库;
什么时候用到
双亲委派机制在很多情况下都会被用到,不仅仅是在使用 import
语句时。
例如,当我们通过 new
关键字创建一个对象时,JVM 会去加载对应的类,此时就会遵循双亲委派机制。
假设我们自定义了一个名为 java.lang.String
的类,当程序尝试创建这个自定义类的对象时,双亲委派机制就会发挥作用。首先,应用程序类加载器不会直接加载这个类,而是将加载请求委派给它的父类加载器扩展类加载器。扩展类加载器也不会加载,继续将请求委派给启动类加载器。由于启动类加载器能够找到并加载 Java 核心库中的 java.lang.String
类,所以就不会使用我们自定义的同名类,从而保证了 Java 核心类的权威性和一致性。
又如,当一个类的静态方法被调用时,JVM 同样会按照双亲委派机制去加载该类。
总之,只要涉及到类的加载,JVM 都会遵循双亲委派机制,以确保类的加载安全和正确,这是 JVM 类加载机制的一个重要原则和保障。
JVM如何知道所需的类
虚拟机设计团队把加载动作放到 JVM 外部实现,以便让应用程序决定如何获取所需的类, JVM 提供了 3 种类加载器:
启动类加载器 (Bootstrap ClassLoader)
(根加载器)
负责加载 JAVA_HOME\lib 目录中的, 或通过-Xbootclasspath 参数指定路径中的, 且被虚拟机认可(按文件名识别,如 rt.jar) 的类。
扩展类加载器 (Extension ClassLoader)
负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类库。
应用程序类加载器 (Application ClassLoader):
(系统加载器)
负责加载用户路径(classpath)上的类库。JVM 通过双亲委派模型进行类的加载, 当然我们也可以通过继承 java.lang.ClassLoader实现自定义的类加载器。