說一下我理解的JVM雙親委派機制,有什麼不對還請指正

一、雙親委派模型

我們知道類載入機制是將一個類從位元組碼檔案轉化為虛擬機器可以直接使用類的過程,但是是誰來執行這個過程中的載入過程,它又是如何完成或者說保障了類載入的準確性和安全性呢?答案就是類載入器以及雙親委派機制。

雙親委派模型的工作機制是:當類載入器接收到類載入的請求時,它不會自己去嘗試載入這個類,而是把這個請求委派給父載入器去完成,只有當父載入器反饋自己無法完成這個載入請求時,子類載入器才會嘗試自己去載入。

我們可以從JDK原始碼中將它的工作機制一窺究竟。

ClassLoader#loadClass(String,boolean)

這是在JDK1。8的java。lang。ClassLoader類中的原始碼,這個方法就是用於載入指定的類。

protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded // 首先,檢查該類是否已經被當前類載入器載入 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System。nanoTime(); try { if (parent != null) { c = parent。loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class。 //如果父類未完成載入,使用當前類載入器去載入該類 long t1 = System。nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun。misc。PerfCounter。getParentDelegationTime()。addTime(t1 - t0); sun。misc。PerfCounter。getFindClassTime()。addElapsedTimeFrom(t1); sun。misc。PerfCounter。getFindClasses()。increment(); } } if (resolve) { resolveClass(c); } return c; } }

透過以上程式碼得出結論:

當類載入器接收到類載入的請求時,首先檢查該類是否已經被當前類載入器載入;

若該類未被載入過,當前類載入器會將載入請求委託給父類載入器去完成;

若當前類載入器的父類載入器為null,會委託啟動類載入器完成載入;

若父類載入器無法完成類的載入,當前類載入器才會去嘗試載入該類。

類載入器分類

啟動類載入器 Bootstrap ClassLoader

啟動類載入器作為所有類載入器的鼻祖,是由C++實現的,不繼承java。lang。ClassLoader類。它在虛擬機器啟動時會由虛擬機器的一段C++程式碼進行載入,所以它沒有父類載入器,在載入完成後,它會負責去載入擴充套件類載入器和應用類載入器。

啟動類載入器用於載入java的核心類,位於JAVA_HOME\lib中,或者被 -Xbootclasspath引數所指定的路徑中,並且是虛擬機器能夠識別的類庫。

擴充套件類載入器 Extension ClassLoader

拓展類載入器繼承於java。lang。ClassLoader類,它的父類載入器是啟動類載入器,而啟動類載入器在java中的顯示就是null。

引⾃ jdk1。8 ClassLoader#getParent() ⽅法的註釋,這個⽅法是⽤於獲取類載入器的⽗類載入器: Returns the parent class loader for delegation。 Some implementations may use null to represent the bootstrap class loader。 This method will return null in such implementations if this class loader’s parent is the bootstrap class loader

擴充套件類載入器負責載入JAVA_HOME\lib\ext目錄中的,或透過java。ext。dirs系統變數指定路徑中的類庫。

注意:擴充套件類載入器僅支援載入被打包為 。jar 格式的位元組碼檔案。

應用類/系統類載入器 Application ClassLoader

JVM 透過雙親委派模型進行類的載入,當然我們也可以透過繼承 java。lang。ClassLoader 實現自定義的類載入器。

說一下我理解的JVM雙親委派機制,有什麼不對還請指正

自定義類載入器 Custom ClassLoader

自定義類載入器繼承於 java。lang。ClassLoader 類,它的父類載入器是應用類載入器。

這是使用者自定義的類載入器,可載入指定路徑的位元組碼檔案。

自定義類載入器需要繼承 java。lang。ClassLoader 類並重寫 findClass方法,用於實現自定義的載入類邏輯。

雙親委派模型的好處

基於雙親委派模型規定的這種帶有優先順序的層次性關係,虛擬機器執行程式時就能夠避免類的重複載入。

當父類類載入器已經載入過類時,如果再有該類的載入請求傳遞到子類載入器,子類載入器執行loadClass方法,然後委託給父類載入器嘗試載入該類,但是父類載入器執行 Class<?> c = findLoadedClass(name);檢查該類是否已經被載入過這一階段就會檢查到該類已經被載入過,直接返回該類,而不會再次載入此類。

雙親委派模型能夠避免核心類篡改。一般我們描述的核心類是 rt。jar、tools。jar 這些由啟動類載入器載入的類,這些類庫在日常開發中被廣泛運用,如果被篡改,後果將不堪設想。

雙親委派模型的不足

由於歷史原因( ClassLoader 類在 JDK1。0 時就已經存在,⽽雙親委派模型是在 JDK1。2 之後才引⼊的),在未引⼊雙親委派模型時,⽤戶⾃定義的類載入器需要繼承 java。lang。ClassLoader 類並重寫 loadClass() ⽅法,因為虛擬機器在載入類時會調⽤ ClassLoader#loadClassInternal(String) ,⽽這個⽅法(原始碼如下)會調⽤⾃定義類載入重寫的 loadClass() ⽅法。⽽在引⼊雙親委派模型後,ClassLoader#loadClass ⽅法實際就是雙親委派模型的實現,如果重寫了此⽅法,相當於打破了雙親委派模型。為了讓⽤戶⾃定義的類載入器也遵從雙親委派模型, JDK新增了 findClass ⽅法,⽤於實現⾃定義的類載入邏輯。

private Class<?> loadClassInternal(String name) throws ClassNotFoundException{ // For backward compatibility, explicitly lock on ‘this’ when // the current class loader is not parallel capable。 if (parallelLockMap == null) { synchronized (this) { return loadClass(name); } } else { return loadClass(name); } }

由於雙親委派模型規定的層次性關係,導致⼦類類載入器載入的類能訪問⽗類類載入器載入的類,⽽⽗類類載入器載入的類⽆法訪問⼦類類載入器載入的類。為了讓上層類載入器載入的類能夠訪問下層類載入器載入的類,或者說讓⽗類類載入器委託⼦類類載入器完成載入請求,JDK 引⼊了執行緒上下⽂類載入器,藉由它來打破雙親委派模型的屏障。

當⽤戶需要程式的動態性,⽐如程式碼熱替換、模組熱部署等時,雙親委派模型就不再適⽤,類載入器會發展為更為複雜的⽹狀結構。

總結

說一下我理解的JVM雙親委派機制,有什麼不對還請指正

最後

在文章的最後作者為大家整理了很多資料!包括java核心知識點+全套架構師學習資料和影片+一線大廠面試寶典+面試簡歷模板+阿里美團網易騰訊小米愛奇藝快手嗶哩嗶哩面試題+Spring原始碼合集+Java架構實戰電子書等等!歡迎關注公眾號:前程有光,領取!