3

面试官:双亲委派模型你了解吗?

 2 years ago
source link: https://zhuanlan.zhihu.com/p/423436230
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

面试官:双亲委派模型你了解吗?

面试官要不你今天来详细讲讲双亲委派机制?

候选者:嗯,好的。

候选者:上次提到了:class文件是通过「类加载器」装载至JVM中的

候选者:为了防止内存中存在多份同样的字节码,使用了双亲委派机制(它不会自己去尝试加载类,而是把请求委托给父加载器去完成,依次向上)

候选者:JDK 中的本地方法类一般由根加载器(Bootstrp loader)装载,JDK 中内部实现的扩展类一般由扩展加载器(ExtClassLoader )实现装载,而程序中的类文件则由系统加载器(AppClassLoader )实现装载。

v2-241ed5c080da67d2a4a7ece5162d1fd4_720w.jpg

候选者:这应该很好理解吧?

面试官:雀食(确实)!

面试官顺着话题,我想问问,打破双亲委派机制是什么意思?

候选者:很好理解啊,意思就是:只要我加载类的时候,不是从APPClassLoader->Ext ClassLoader->BootStrap ClassLoader 这个顺序找,那就算是打破了啊

候选者:因为加载class核心的方法在LoaderClass类的loadClass方法上(双亲委派机制的核心实现)

候选者:那只要我自定义个ClassLoader,重写loadClass方法(不依照往上开始寻找类加载器),那就算是打破双亲委派机制了。

面试官:这么简单?

候选者:嗯,就是这么简单

面试官那你知道有哪个场景破坏了双亲委派机制吗?

候选者:最明显的就Tomcat啊

面试官:详细说说?

候选者:在初学时部署项目,我们是把war包放到tomcat的webapp下,这意味着一个tomcat可以运行多个Web应用程序(:

候选者:是吧?

面试官:嗯..

候选者:那假设我现在有两个Web应用程序,它们都有一个类,叫做User,并且它们的类全限定名都一样,比如都是com.yyy.User。但是他们的具体实现是不一样的

候选者:那么Tomcat是如何保证它们是不会冲突的呢?

候选者:答案就是,Tomcat给每个 Web 应用创建一个类加载器实例(WebAppClassLoader),该加载器重写了loadClass方法,优先加载当前应用目录下的类,如果当前找不到了,才一层一层往上找(:

候选者:那这样就做到了Web应用层级的隔离

面试官嗯,那你还知道Tomcat还有别的类加载器吗?

候选者:嗯,知道的

候选者:并不是Web应用程序下的所有依赖都需要隔离的,比如Redis就可以Web应用程序之间共享(如果有需要的话),因为如果版本相同,没必要每个Web应用程序都独自加载一份啊。

候选者:做法也很简单,Tomcat就在WebAppClassLoader上加了个父类加载器(SharedClassLoader),如果WebAppClassLoader自身没有加载到某个类,那就委托SharedClassLoader去加载。

候选者:(无非就是把需要应用程序之间需要共享的类放到一个共享目录下嘛)

面试官:嗯..

候选者:为了隔绝Web应用程序与Tomcat本身的类,又有类加载器(CatalinaClassLoader)来装载Tomcat本身的依赖

候选者:如果Tomcat本身的依赖和Web应用还需要共享,那么还有类加载器(CommonClassLoader)来装载进而达到共享

候选者:各个类加载器的加载目录可以到tomcat的catalina.properties配置文件上查看

候选者:我稍微画下Tomcat的类加载结构图吧,不然有点抽象

面试官:嗯,还可以,我听懂了,有点意思。

面试官顺便,我想问下,JDBC你不是知道吗,听说它也是破坏了双亲委派模型的,你怎么理解的。

候选者:Eumm,这个有没有破坏,见仁见智吧。

候选者:JDBC定义了接口,具体实现类由各个厂商进行实现嘛(比如MySQL)

候选者:类加载有个规则:如果一个类由类加载器A加载,那么这个类的依赖类也是由「相同的类加载器」加载。

候选者:我们用JDBC的时候,是使用DriverManager进而获取Connection,DriverManager在java.sql包下,显然是由BootStrap类加载器进行装载

候选者:当我们使用DriverManager.getConnection()时,得到的一定是厂商实现的类。

候选者:但BootStrap ClassLoader会能加载到各个厂商实现的类吗?

候选者:显然不可以啊,这些实现类又没在java包中,怎么可能加载得到呢

面试官:嗯..

候选者:DriverManager的解决方案就是,在DriverManager初始化的时候,得到「线程上下文加载器」

候选者:去获取Connection的时候,是使用「线程上下文加载器」去加载Connection的,而这里的线程上下文加载器实际上还是App ClassLoader

候选者:所以在获取Connection的时候,还是先找Ext ClassLoader和BootStrap ClassLoader,只不过这俩加载器肯定是加载不到的,最终会由App ClassLoader进行加载

面试官:嗯..

候选者:那这种情况,有的人觉得破坏了双亲委派机制,因为本来明明应该是由BootStrap ClassLoader进行加载的,结果你来了一手「线程上下文加载器」,改掉了「类加载器」

候选者:有的人觉得没破坏双亲委派机制,只是改成由「线程上下文加载器」进行类加载,但还是遵守着:「依次往上找父类加载器进行加载,都找不到时才由自身加载」。认为”原则”上是没变的。

面试官:那我了解了

本文总结

  • 前置知识:JDK中默认类加载器有三个:AppClassLoader、Ext ClassLoader、BootStrap ClassLoader。AppClassLoader的父加载器为Ext ClassLoader、Ext ClassLoader的父加载器为BootStrap ClassLoader。这里的父子关系并不是通过继承实现的,而是组合。
  • 什么是双亲委派机制:加载器在加载过程中,先把类交由父类加载器进行加载,父类加载器没找到才由自身加载。
  • 双亲委派机制目的:为了防止内存中存在多份同样的字节码(安全)
  • 类加载规则:如果一个类由类加载器A加载,那么这个类的依赖类也是由「相同的类加载器」加载。
  • 如何打破双亲委派机制:自定义ClassLoader,重写loadClass方法(只要不依次往上交给父加载器进行加载,就算是打破双亲委派机制)
  • 打破双亲委派机制案例:Tomcat
    • 为了Web应用程序类之间隔离,为每个应用程序创建WebAppClassLoader类加载器
    • 为了Web应用程序类之间共享,把ShareClassLoader作为WebAppClassLoader的父类加载器,如果WebAppClassLoader加载器找不到,则尝试用ShareClassLoader进行加载
    • 为了Tomcat本身与Web应用程序类隔离,用CatalinaClassLoader类加载器进行隔离,CatalinaClassLoader加载Tomcat本身的类
    • 为了Tomcat与Web应用程序类共享,用CommonClassLoader作为CatalinaClassLoader和ShareClassLoader的父类加载器
    • ShareClassLoader、CatalinaClassLoader、CommonClassLoader的目录可以在Tomcat的catalina.properties进行配置
  • 线程上下文加载器:由于类加载的规则,很可能导致父加载器加载时依赖子加载器的类,导致无法加载成功(BootStrap ClassLoader无法加载第三方库的类),所以存在「线程上下文加载器」来进行加载。

我最近一直在连载《对线面试官》系列,目前已经连载38篇啦!一个说人话的面试系列!

【大厂面试知识点】、【简历模板】、【原创文章】电子书,共有1263页

我把这些上传到网盘,你们有需要直接下载就好了。做到这份上了,不会还想白嫖吧点赞转发又不用钱。

链接:pan.baidu.com/s/1pQTuKBYs… 密码:3wom

收藏等于白嫖,点赞才是真情!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK