专栏导航
目录
前言
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了双亲委派机制、父类加载器、双亲委派机制的主要作用、双亲委派机制常见问题等内容。
一、双亲委派机制
Java虚拟机中有多个类加载器,双亲委派机制(Parent Delegation Mechanism)是Java类加载器(ClassLoader)中的一个核心特性,它主要解决了类加载过程中类由谁来加载的问题。
1.双亲委派机制详解
双亲委派机制的核心思想是:当一个类加载器接收到加载类的请求时,它首先不会自己去尝试加载,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器(Bootstrap ClassLoader)中去,只有当父类加载器无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。
当一个类加载器接收到加载类的任务时,会自底向上查找是否加载过再由顶向下进行加载。
在类加载过程中,类加载器首先会检查是否已加载指定类。如果已加载,则直接返回对应的Class对象,否则会将加载请求委派给父类加载器。这种自底向上的查找过程称为"向上查找 ",而自顶向下的加载过程则称为"向下委派"。
双亲委派机制作为一种严谨的类加载机制,确保了类的一致性和准确性,避免了类的重复加载。当所有父类加载器无法找到所需的类时,当前类加载器将承担起加载的责任,这看似是从顶向下的加载尝试。值得注意的是,向下委派加载不仅是一种责任转嫁,更体现了类加载的优先级。
2.父类加载器
在Java中,每个类加载器都有一个父类加载器(Parent ClassLoader),并不是继承关系,可以理解为它的上级。
在Java类加载器的体系中,应用程序类加载器(Application ClassLoader)的父类加载器是扩展类加载器(Extension ClassLoader)。而扩展类加载器的父类加载器并未明确定义,但在实际的代码逻辑中,扩展类加载器会将启动类加载器(Bootstrap Class Loader)视为其父类加载器进行处理。由于启动类加载器由C++编写,不存在父类加载器。
ClassLoader部分源码:
3.双亲委派机制的主要作用
双亲委派机制的主要作用有:
- 保证类加载的安全性:由于双亲委派机制中,顶层的类加载器(如Bootstrap ClassLoader)负责加载核心类库,如java.lang包中的类。这种设计可以避免恶意代码替换核心类库,如java.lang.String,从而确保核心类库的完整性和安全性。
- 避免重复加载:通过双亲委派机制,如果一个类已经被一个类加载器加载过,那么其他的类加载器就无需再次尝试加载,这样可以避免同一个类被多次加载的情况。
这种机制的优点在于它可以有效地避免类的重复加载,同时也可以保证核心类库的安全性。但是,它也有一些局限性,例如可能会影响到类的可见性和可移植性。
案例:
自定义String类(java.lang包下):
java
package lang;
public class String {
static {
System.out.println("自定义String类");
}
}
获取类加载器:
java
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader classLoader = Demo1.class.getClassLoader();
Class<?> aClass = classLoader.loadClass("java.lang.String");
System.out.println(aClass.getClassLoader());
}
}
运行结果(无法获取启动类加载器):
这种设计避免了恶意代码替换核心类库。
二、双亲委派机制常见问题
- 类的双亲委派机制是什么?
- 当一个类加载器去加载某个类的时候,会自底向上查找是否加载过,如果加载过就直接返回,如果一直到最顶层的类加载器都没有加载,再由顶向下进行加载。
- 应用程序类加载器的父类加载器是扩展类加载器,扩展类加载器的父类加载器是启动类加载器。
- 双亲委派机制的主要作用:避免恶意代码替换JDK中的核心类库,确保核心类库的完整性和安全性;避免一个类重复地被加载。
- 如果一个类重复出现在三个类加载器的加载位置,应该由谁来加载?
- 启动类加载器负责加载核心类库,如java.lang包中的类,其优先级最高。在双亲委派机制下,每个类加载器在接收到加载请求时,首先会自底向上查找是否已加载过该类。若已加载,则直接返回Class对象,否则,将加载请求委派给父类加载器。
- 在自己的项目中去创建一个java.lang.String类,会被加载吗?
- 由于java.lang.String属于核心类库,由启动类加载器负责加载。因此,在自己的项目中尝试创建该类将会失败,因为启动类加载器已经加载了rt.jar包中的String类。
- 这几个类加载器彼此之间存在关系吗?
- 应用类加载器的父类加载器是扩展类加载器。而扩展类加载器没有明确的父类加载器,但在实际逻辑中,它会将启动类加载器视为其父类加载器。这种层次结构确保了核心类库的安全性和完整性。
总结
JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了双亲委派机制、父类加载器、双亲委派机制的主要作用、双亲委派机制常见问题等内容,希望对大家有所帮助。