JVM双亲委派机制
Java 虚拟机(JVM)的双亲委派机制是类加载的一种设计模式,用于确保 Java 应用程序中的类加载过程按照一定的顺序进行,以避免类的重复加载和潜在的类冲突。它的核心思想是,当一个类加载器需要加载某个类时,它首先会将该请求委派给父加载器,而不是自己直接去加载类。具体来说,JVM 的类加载器层次结构是以父子关系组织的,顶层是根类加载器(Bootstrap ClassLoader),然后是扩展类加载器(Extension ClassLoader)和应用类加载器(AppClassLoader)。
双亲委派机制的工作原理
-
请求处理顺序:每个类加载器都会先将类加载请求传递给父加载器(即父类加载器),如果父类加载器无法加载该类,才会由当前加载器自己来加载。这个顺序保证了父加载器总是优先于子加载器加载类。
-
防止类的重复加载:因为父加载器优先加载类,子加载器如果发现父加载器已经加载过该类,就不再重复加载。这样可以避免类的冲突或重复定义,确保一个类在 JVM 中只有唯一的实例。
-
加载过程:
- 根类加载器(Bootstrap ClassLoader):加载 JDK 核心类库(例如
java.lang.*
等)。 - 扩展类加载器(Extension ClassLoader):加载 JDK 的扩展类库,通常位于
lib/ext
目录下。 - 应用类加载器(AppClassLoader):加载应用程序的类路径下的类,包括
classpath
中的类。
- 根类加载器(Bootstrap ClassLoader):加载 JDK 核心类库(例如
双亲委派机制的优势
-
类冲突的避免:通过先委派给父类加载器,确保了核心类库不会被用户自定义类覆盖。例如,
java.lang.String
类的加载请求会被委派给根类加载器,避免了应用类加载器加载自己定义的String
类,防止了冲突。 -
系统的稳定性:通过优先加载核心和标准库类,确保了 JDK 提供的类库始终是最新和最可靠的,而用户自己的类则在此基础上加载,保持了系统的稳定性和安全性。
-
类加载的高效性:因为父类加载器先加载,如果已经加载过某个类,则子加载器无需再加载,提高了加载效率。
双亲委派机制的实现
JVM 中的类加载器实现了 ClassLoader
类,ClassLoader
类有一个重要的方法 loadClass(String name)
,它是类加载的核心方法。默认情况下,loadClass
会先尝试委派给父类加载器加载类:
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 委派给父类加载器加载
Class<?> c = findLoadedClass(name);
if (c == null) {
if (parent != null) {
c = parent.loadClass(name);
}
}
if (c == null) {
// 如果父加载器不能加载,再由当前加载器来加载
c = findClass(name);
}
return c;
}
示例:
假设有一个类 MyClass
,加载过程如下:
- 请求加载
MyClass
:应用类加载器(AppClassLoader
)收到加载请求。 - 父加载器的委派:应用类加载器会首先将请求委派给扩展类加载器(
ExtClassLoader
),再由扩展类加载器委派给根类加载器(Bootstrap ClassLoader
)。 - 根类加载器检查:根类加载器负责加载核心类库,如
java.lang.*
。如果MyClass
不属于核心类库,它会返回null
,告知扩展类加载器无法加载。 - 扩展类加载器检查:扩展类加载器检查其路径(如
lib/ext
),如果仍然找不到类MyClass
,则返回null
。 - 应用类加载器加载:如果以上步骤都不能加载
MyClass
,应用类加载器会自己去类路径中查找并加载该类。
它确保了类加载的顺序性、避免了类冲突、提高了加载效率。通过将加载任务委派给父加载器,Java 保证了核心类库始终优先于应用类加载,从而保持了系统的稳定性和一致性。
双亲委派机制的异常处理
双亲委派机制并不是万能的。在某些情况下,我们需要修改或自定义类加载器的行为。例如,若我们希望自定义某些类的加载顺序或加载特定目录下的类,就需要通过继承 ClassLoader
类来实现。
自定义类加载器
假设我们要创建一个新的类加载器,专门加载某个特定路径下的类,并且不希望将加载请求委派给父加载器。我们可以通过重写 findClass
方法来控制加载过程,但仍然可以保留双亲委派机制的优势。自定义类加载器的典型示例如下:
public class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
// 查找指定路径下的类文件并加载
byte[] classData = loadClassData(name);
if (classData == null) {
throw new ClassNotFoundException(name);
}
return defineClass(name, classData, 0, classData.length);
}
private byte[] loadClassData(String className) {
// 从指定路径加载类的字节码
// 这个方法会从 classPath 路径加载字节码文件
String path = classPath + "/" + className.replace('.', '/') + ".class";
// 读取文件内容并返回字节数组
// 示例代码省略实际的文件读取
return null;
}
}
调整双亲委派机制
在自定义类加载器时,可能会有需求希望绕过父加载器。比如,当我们有多个不同版本的第三方库时,可能需要先加载本地版本的类,而不委派给父加载器。为此,我们可以在 loadClass
方法中修改委派顺序。
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 不委派给父加载器,直接由当前加载器处理
if (name.startsWith("com.mycompany")) {
return findClass(name);
}
// 否则,委派给父加载器
return super.loadClass(name);
}
这种调整通常用于避免加载错误的类,或者处理特殊版本的类冲突。
双亲委派机制的优缺点
优点
- 稳定性:通过委派给父加载器,保证了核心类(如
java.lang
类)始终是由系统类加载器加载的,从而避免了核心类被应用程序中的类覆盖的情况。 - 减少重复加载:父类加载器已经加载过某个类,子类加载器就不会再进行加载,这样可以提高效率并避免类加载冲突。
- 避免类的篡改:由于父类加载器会优先加载类,应用类加载器无法随意修改系统类库的实现,有助于防止不安全的代码或漏洞。
缺点
- 灵活性差:双亲委派机制的设计虽然能确保稳定性,但它会限制类加载的灵活性。如果有需要动态加载不同版本的同一类的场景,双亲委派机制可能就不适用了。
- 性能开销:每次加载类时都要经过父类加载器的层层委派,可能会产生一些性能开销,尤其是在层级比较深的类加载器结构中,可能会影响类加载速度。
- 特定需求处理复杂:当我们需要动态加载不同来源的类时(比如插件框架),可能需要自定义加载器并打破双亲委派机制,这样需要更多的控制逻辑,增加了代码复杂度。
双亲委派机制与 ClassLoader 的多态性
Java 的类加载器机制是多态的,它允许通过自定义加载器实现灵活的加载策略。ClassLoader
是一个抽象类,我们可以根据实际需求选择不同的加载器,甚至组合多个加载器。ClassLoader
的多态性使得我们可以为不同的类路径配置不同的加载器。
例如,如果我们有两个插件系统,一个是内部开发的插件,一个是第三方的插件,我们可以为每个插件系统创建不同的类加载器。这样即使有相同的类名,两个系统也不会互相冲突,确保了插件的独立性。
public class PluginClassLoader extends ClassLoader {
// 插件加载逻辑
}
public class InternalPluginClassLoader extends ClassLoader {
// 内部插件加载逻辑
}
现实中的应用
- Web 应用服务器:比如 Tomcat、Jetty 等,通常会使用双亲委派机制来加载应用程序的类库。Tomcat 会为每个 Web 应用创建独立的类加载器,同时采用双亲委派机制来加载系统类库和核心类。
- OSGi 框架:OSGi 是一个模块化框架,它通过自定义类加载器来实现动态模块加载,同时有时也需要修改双亲委派机制以适应动态模块的加载需求。
- 插件架构:在一些大型的应用程序中(如 IDE、游戏引擎等),插件系统的设计通常会依赖于类加载器来加载插件。为避免插件和主程序的类库冲突,插件类加载器需要打破父类加载器的限制,实现独立加载。
原文地址:https://blog.csdn.net/weixin_45710998/article/details/144103597
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!