大家好,我是锋哥。今天分享关于JVM双亲委派模型的JVM面试题,希望对大家有帮助;
什么是双亲委派模型?
双亲委派模型针对的是 Java 虚拟机中三个类加载器的,这三个类加载器分别是:
- 启动类加载器(Bootstrap ClassLoader)
- 扩展类加载器(Extension ClassLoader)
- 应用程序类加载器(Application ClassLoader)
如下图所示:
上面这几类类加载器构成了不同的层次结构,当我们需要加载一个类时,子类加载器并不会马上去加载,而是依次去请求父类加载器加载,一直往上请求到最高类加载器:启动类加载器。当启动类加载器加载不了的时候,依次往下让子类加载器进行加载。这就是双亲委派模型。
1000道 互联网大厂Java工程师 精选面试题-Java资源分享网1000道 互联网大厂Java工程师 精选面试题http://java.python222.com/article/971
双亲委派模型的缺陷?
在双亲委派模型中,子类加载器可以使用父类加载器已经加载的类,而父类加载器无法使用子类加载器已经加载的。这就导致了双亲委派模型并不能解决所有的类加载器问题。
Java 提供了很多外部接口,这些接口统称为 Service Provider Interface, SPI ,允许第三方实现这些接口,而这些接口却是 Java 核心类提供的,由 Bootstrap Class Loader 加载,而一般的扩展接口是由Application Class Loader 加载的, Bootstrap Class Loader 是无法找到 SPI 的实现类的,因为它只加载 Java 的核心库。它也不能代理给 Application Class Loader ,因为它是最顶层的类加载器。
介绍下双亲委派机制的三次破坏?
虽然双亲委派机制是 Java 强烈推荐给开发者们的类加载器的实现方式,但是并没有强制规定你必须就要这么实现,所以,它一样也存在被破坏的情况,实际上,历史上一共出现三次双亲委派机制被破坏的情况:
- 双亲委派机制第一次被破坏发生在双亲委派机制出现之前,由于双亲委派机制 JDK 1.2 之后才引用的,但类加载的概念在 Java 刚出现的时候就有了,所以引用双亲委派机制之前,设计者们必须兼顾开发者们自定义的一些类加载器的代码,所以在 JDK 1.2 之后的 java.lang.ClassLoader 中添加了一个新的 findClass 方法,引导用户编写类加载器逻辑的时候重写这个 findClass 方法,而不是基于 loadClass编写。
- 双亲委派机制第二次被破坏是由于它自己模型导致的,由于它只能向上(基础)加载,越基础的类越由上层加载器加载,所以如果基础类型又想要调用用户的代码,该怎么办?这也就是我们上面那个问题所说的 SPI 机制。那么 JDK 团队是如何做的呢?它们引用了一个 线程上下文类加载器 (Thread Context ClassLoader),这个类加载器可以通过 java.lang.Thread 类的 setContextClassLoader 进行设置,如果创建时线程还未设置,它将会从父线程中继承,如果全局没 有设置类加载器的话,这个 ClassLoader 就是默认的类加载器。这种行为虽然是一种犯规行为,但是 Java 代码中的 JNDI、JDBC 等都是使用这种方式来完成的。直到 JDK 6 ,引用了 java.util.ServiceLoader,使用 META-INF/services + 责任链的设计模式,才解决了 SPI 的这种加载机制。
- 双亲委派机制第三次被破坏是由于用户对程序的动态需求使热加载、热部署的引入所致。由于时代的变化,我们希望 Java 能像鼠标键盘一样实现热部署,即时加载(load class),引入了 OSGI,OSGI 实现热部署的关键在于它自定义类加载器机制的实现,OSGI 中的每一个 Bundle 也就是模块都有一个自己的类加载器。当需要更换 Bundle 时,就直接把 Bundle 连同类加载器一起替换掉就能够实现热加载。在 OSGI 环境下,类加载器不再遵从双亲委派机制,而是使用了一种更复杂的加载机制。