谈谈对JVM的双亲委派模型的理解

Java面试 潘老师 6个月前 (09-05) 127 ℃ (0) 扫码查看

Java面试题:谈谈对JVM的双亲委派模型的理解

得分点:

启动类加载器、扩展类加载器、应用程序类加载器。

标准回答:

对于JDK 8及其之前版本的Java应用,通常会使用以下三个系统提供的类加载器来进行加载:

  1. 启动类加载器:这个类加载器负责加载存放在<java_home>\lib目录,或者被-Xbootclasspath参数所指定的路径中存放的,Java虚拟机能够识别的类库加载到虚拟机的内存中。需要注意的是,Java虚拟机会按照文件名识别类库,例如rt.jartools.jar,对于名字不符合的类库即使放在lib目录中也不会被加载。启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器去处理,那直接使用null代替即可,即让java.lang.ClassLoader.getClassLoader()返回null
  2. 扩展类加载器:这个类加载器是在类sun.misc.Launcher$ExtClassLoader中以Java代码的形式实现的。它负责加载<java_home>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。由于扩展类加载器是由Java代码实现的,开发者可以直接在程序中使用扩展类加载器来加载Class文件。
  3. 应用程序类加载器:这个类加载器由sun.misc.Launcher$AppClassLoader来实现。它负责加载用户类路径(ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。

这三个类加载器之间通常以双亲委派模型的方式协作,双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。这个模型的工作过程是,如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去完成加载。

使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。例如,类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都能够保证是同一个类。

加分回答:

双亲委派模型并不是一个具有强制性约束的模型,而是Java设计者推荐给开发者们的类加载器实现方式。在Java的世界中,大部分的类加载器都遵循这个模型,但也有例外的情况。双亲委派模型的工作原理是在加载类时,首先尝试委派给父加载器加载,如果父加载器无法加载则尝试自己加载。这种模型有助于保持类加载的层次结构和类的隔离性。

另外,双亲委派模型的破坏情况也有发生,例如:

  1. 双亲委派模型的第一次“被破坏”:在双亲委派模型出现之前,已有用户自定义类加载器的代码,为了兼容这些代码,引入了findClass()方法来实现自定义加载逻辑。
  2. 双亲委派模型的第二次“被破坏”:由于一些特殊需求,引入了线程上下文类加载器(Thread Context ClassLoader),允许在特定情况下使用不同的类加载器。
  3. 双亲委派模型的第三次“被破坏”:一些场景下,用户希望实现动态性,例如代码热替换和模块热部署,导致需要绕过双亲委派模型来实现自定义加载逻辑。这可能会引入更复杂的类加载器结构,如OSGi中的类加载器机制。

总的来说,双亲委派模型是一个很有用的模型,有助于保持类加载的一致性和隔离性,但在一些特殊情况下,为了兼容已有代码或实现特定需求,可能需要绕过这一模型。然而,开发者在这些情况下需要谨慎操作,以确保不引入类加载冲突和安全问题。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/javainterview/8720.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】