面試官:雙親委派模型你瞭解嗎?

Java3y發表於2021-10-20

面試官要不你今天來詳細講講雙親委派機制?

候選者:嗯,好的。

候選者:上次提到了:class檔案是通過「類載入器」裝載至JVM中的

候選者:為了防止記憶體中存在多份同樣的位元組碼,使用了雙親委派機制(它不會自己去嘗試載入類,而是把請求委託給父載入器去完成,依次向上)

候選者:JDK 中的本地方法類一般由根載入器(Bootstrp loader)裝載,JDK 中內部實現的擴充套件類一般由擴充套件載入器(ExtClassLoader )實現裝載,而程式中的類檔案則由系統載入器(AppClassLoader )實現裝載。

候選者:這應該很好理解吧?

面試官:雀食(確實)!

面試官順著話題,我想問問,打破雙親委派機制是什麼意思?

候選者:很好理解啊,意思就是:只要我載入類的時候,不是從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無法載入第三方庫的類),所以存在「執行緒上下文載入器」來進行載入。

歡迎關注我的微信公眾號【Java3y】來聊聊Java面試,對線面試官系列持續更新中!

面試官:雙親委派模型你瞭解嗎?

【對線面試官-移動端】系列 一週兩篇持續更新中!

【對線面試官-電腦端】系列 一週兩篇持續更新中!

相關文章