類載入器從虛擬機器規範的角度來說,JVM 分為 引導類載入器 Bootstrap ClassLoader和 非引導類載入器 兩種類型的類載入器。不同的虛擬機器的實現不盡相同,本文以最主流的 HotS
2021-06-22 19:53:38
類載入器
從虛擬機器規範的角度來說,JVM 分為 引導類載入器 Bootstrap ClassLoader和 非引導類載入器 兩種類型的類載入器。不同的虛擬機器的實現不盡相同,本文以最主流的 HotSpot 虛擬機器為例。
類載入器的分類
主要包含以下四種類載入器,
Bootstrap ClassLoader:引導類載入器 引導類載入器是由 C/C++ 語言編寫的,無法作為物件被程式所引用 主要用來載入 核心類庫 (rt.jar) 中的類 為安全考慮,啟動類載入器只加載包名為 java、javax、sun 等開頭的類 載入 ExtClassLoader,AppClassLoader 併為它們指定父載入器 引導類載入器沒有父載入器Extension ClassLoader:擴展類載入器 由 sun.misc.Launcher$ExtClassLoader 實現,父載入器是 啟動類載入器 負責載入 JAVA_HOME/lib/ext 目錄中,或者被 java.ext.dirs 系統變數指定的路徑中的所有類庫Application ClassLoader:系統類載入器 由 sun.misc.Launcher$AppClassLoader 實現,父載入器是 擴展類載入器 是 ClassLoader.getSystemClassLoader() 的返回值,因此也稱為系統類載入器 如果程式中沒有自定義任何其他的類載入器,我們自己編寫的類都是由系統類載入器進行載入的User ClassLoader:自定義類載入器 使用者自定義的類載入器可以由使用者實現並獲取任意來源的二進位制位元組碼進行載入除了啟動類載入器外,其他的類載入器都繼承自 java.lang.ClassLoader,類載入具體邏輯封裝在 loadClass() 方法中。
自定義類載入器
自定義一個類載入非常容易,只需要繼承 java.lang.ClassLoader。在 jdk1.2 之前,繼承 ClassLoader 時,要重寫 loadClass() 方法,來實現自定義類的載入邏輯。在 jdk1.2 後,建議把自定義類載入邏輯寫在 findClass() 方法中,而不是重寫 loadClass()。
導致這種變化的原因是因為雙親委派機制的出現,我們後面會詳細說明。
如果沒有過於複雜的需求,可以直接繼承 URLClassLoader 類實現自定義的類載入器,更加簡單。
如何獲取類載入器
如果我們要檢視一個類是由哪個類載入器進行載入的,可以通過如下幾種方法獲取類載入。
方式 1 :通過 class 物件的 getClassLoader() 方法
方式 2 :執行緒獲取上下文類載入器
方式 3:獲取系統類載入器
類載入器的名稱空間
JVM 規範中規定,每個類載入器都有一個屬於自己的名稱空間。即使我們使用不同的類載入器載入同一個類,也會導致這兩個類不相等。
比如下面的例子中,我們自定義了一個類載入器 myClassLoader,並用它載入 ClassLoaderDemo02 這個類並獲取它的一個例項 obj。然後我們用 instanceof 比較得到的結果卻是 false。
這是因為 ClassLoaderDemo02 本身是用 AppClassLoader 載入的,而 obj 是自定義類載入器載入的。
實際上,這種情況並不是我們所希望的,可能會導致某些錯誤。下面介紹的雙親委派模型就可以很好地解決這個問題。
雙親委派模型
雙親委派的原理
預設情況下,一個限定名的類只會被一個類載入器載入並解析使用,這樣在使用時就不會產生歧義。
雙親委派機制 (Parents Delegation Model): 如果一個類載入器收到了類載入的請求,它不會自己先載入,而是將載入請求委託給父載入器載入;如果父載入器還有父載入器,繼續向上委託,直到委託到最頂層的 啟動類載入器。
如果父載入器可以完成類載入器請求,就成功返回;如果父載入器無法完成請求,子類載入器才會嘗試自己載入。
父載入器和子載入器之間不是繼承關係,而是通過 組合來實現的一種邏輯關係。
雙親委派的實現
雙親委派的實現寫在了 loadClass 方法中。
破壞雙親委派
雙親委派機制 並不是一個強制性的約束模型,這意味著這種機制可以被破壞。比如上面的 類載入器的名稱空間 一節中,重寫了 loadClass 方法其實就是對雙親委派的一次破壞,所以在 JDK1.2 引入雙親委派機制後,建議重寫 findClass 而不是 loadClass,來避免破壞這種機制。
第一次破壞
上面已經提到,JDK1.2 之前,可以通過重寫 loadClass() 方法,來破壞雙親委派模型。
第二次破壞
第二次破壞和 SPI(Service Provider Interface) 有關,典型的例子就是 JDBC。
各個廠商都需要實現自己的驅動,實現同一個介面 java.sql.Driver (該類由啟動類載入器載入) ,但是啟動類載入器不識別這些驅動。
為了解決這個問題,引入了 執行緒上下文載入器,該載入器可以通過 Thread.setContextClassLoader() 設定,如果執行緒還沒有創建,會從父執行緒繼承一個載入器;如果全局都沒有設定過載入器,執行緒上下文載入器預設是 應用程式類載入器。
這樣就相當於用父類載入器來請求子類載入器去完成類載入,已經違背了雙親委派模型的自底向上的邏輯原則。
第三次破壞
第三次破壞與 程式的動態性相關,比如熱部署等。在這種情況下,樹形結構的雙親委派顯然無法滿足這種複雜的需求,類載入變成了複雜的網狀結構。
比如 JDK9 開始引入了 模組化,這也在一定程度上破壞了雙親委派機制。並且類載入的結構和實現方式都有了變化,一個類如果歸屬於某個模組,會優先委派給載入該模組的類載入器去載入,而不是直接委派給父載入器載入。
如何獲取?
轉發分享此文,後臺私信小編:「資料」加V即可獲取。(注:轉發分享,感謝大家)
相關文章
類載入器從虛擬機器規範的角度來說,JVM 分為 引導類載入器 Bootstrap ClassLoader和 非引導類載入器 兩種類型的類載入器。不同的虛擬機器的實現不盡相同,本文以最主流的 HotS
2021-06-22 19:53:38
美再一次出手了!從谷歌服務到不讓美企提供晶片,當時華為還以為這只是晶片問題,那沒關係,海思設計晶片,鴻蒙也拿出手了,可後來特不靠譜接二連三的操作不僅使得華為猝不及防,就是國內
2021-06-22 19:52:27
雖說中國已是全球最大的汽車市場,但是大排量超級跑車領域,一直還處於空白階段。不過,隨著紅旗S9的到來,它將徹底改變中國品牌量產超跑「零」的記錄。繼上海車展之後,紅旗官方正式
2021-06-22 19:52:03
如果你是一個iPhone深度使用者,那麼你一定知道蘋果手機上有一個「小白點」(或者叫「懸浮球」),這個小白點的全稱叫「AssistiveTouch」,該功能主要是為了方便那些類似於肌無力的患
2021-06-22 19:33:10
華為作為國內最大的手機巨頭,憑藉產品和技術的優勢,在全球手機市場取得不俗的成績。其銷量甚至一度超越三星,登上全球第一寶座。不過,受到晶片禁令影響,華為自研的麒麟晶片無法生
2021-06-22 19:31:46
這段時間,不少讀者都發現,中國移動、中國電信公佈的伺服器裝置採購名單中都沒有出現華為的身影。這一情況一度在網路上引起熱議,很多人分析,華為是因為受到晶片制裁,晶片儲備不足
2021-06-22 19:30:41