選單

JVM筆記二雙親委派機制

JVM的雙親委派機制

JVM類載入器是什麼機制?為什麼使用這種機制(這種機制的好處是什麼)?說下類載入流程?用程式碼驗證類載入機制。為什麼要破壞類的這種載入機制?

我們已經知道了JVM類載入器的四種載入機制,那麼這四種載入機制是怎麼個載入過程呢?我們再來看看類載入器的圖例:

JVM筆記二雙親委派機制

圖一:雙親委派機制圖例

先來看看類和類載入器:

類和類載入器

對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在Java虛擬機器中的唯一性。當我們比較兩個類是否相等的時候,前提是:只有在這兩個類是由同一個類載入器載入為前提下才有意義的。

JVM筆記二雙親委派機制

透過上面文字描述,我們知道了,在JVM中,類可以透過不同類載入器載入的。這個資訊很重要。既然類可以透過不同載入器載入後,使其不與其他類equals。那麼,是否可以自己寫個java。lang。string類呢?我們都知道,自己寫的類是appClassLoader載入到JVM的;jvm原生的string類是由bootstrapClassLoader載入的。這兩個類是不同的載入器載入,是不eqs的。那麼實際上可以嗎?答案是:不可以(下文凱哥Java(wxID:kaigejava)會透過程式碼來證實)。為什麼不可以呢?因為雙親委派機制的緣故。

雙親委派機制

如果從JVM角度來講的話,類的載入器只有兩種:啟動類載入器。這個類是C++寫的,是JVM虛擬機器自身的一部分;另一種就是所有其他類的類載入器了。是Java寫的,獨立於虛擬機器外部的,而且都是繼承於:java。lang。ClassLoader的。

從我們Java開發任意角度來看的話,就可以分為四種類載入器了。這裡先不具體概述了,在下文會介紹的。

在圖一的圖例中展示的類載入器之間層次管理,就被稱之為雙親委派模型(Parents Delegation Model)。

雙親委派機制藥圈,除了頂層的類載入器(Bootstrap)外,其餘的類載入器都應該有自己的父類載入器。PS:透過上一篇《JVM學習筆記之類裝載器-ClassLoader》的最後,我們透過程式碼演示了,自定義類的父載入器是appClassLoader,appClassLoader的父載入器是擴充套件類載入器。

雙親委派機制的執行過程:

如果一個類載入器收到了類載入的請求,這個類載入器不會先嚐試載入這個類,而是會先把這個請求委派給自己的父類載入器去完成,在每個層次的類載入器都是依此類推的。因此所有的類載入器請求其最後都應該被委派到頂層的啟動類載入器中(Bootstrap),只有當父類的載入器在自己管轄範圍內(文末會介紹每個類載入器管理範圍的)沒有找到所需要的類的時候,子類載入器才會嘗試自己去載入的,如果自己管理範圍內也找不到需要載入的就會丟擲:ClassNotFoundException 這個異常了。這就是為什麼如果我們自己寫java。lang。string類的時候,是不行的。

我們可以自己嘗試著建立一個java。lang。String類,類裡面只有一個main方法,執行會是什麼樣的呢?

JVM筆記二雙親委派機制

如上圖:執行的時候,報錯了。根據提示,我們就能知道,自己寫的string類在啟動的時候,類載入了,向上父類委派,最終委派到啟動類載入器了,啟動了載入器發現自己管轄的範圍內存在String類,然後呼叫Main方法的時候,發現沒有這個方法,就報錯了。這個流程執行的簡圖:

雙親委派流程示意圖

JVM筆記二雙親委派機制

圖二:雙親委派機制流程圖

雙親委派機制的好處

JVM為什麼要使用雙親委派這種機制呢?有什麼好處呢?透過我們自己寫的java。lang。string這個類啟動報錯中,我們就可以得出以下雙親委派的優點:

1:保證了JVM提供的核心類不被篡改,保證class執行安全

比如上文的string類,無論哪個載入器要載入這個類的話,由於雙親委派機制,最終都會交由最頂層的啟動類載入器來載入,這樣保證了string類在各種類載入器環境中,都是同一個類。試想下,沒有雙親委派機制的話,各個載入器自己載入string類,有可能不同類載入器載入的string方法不一樣,那樣的話,我們的程式是不是就會一片混亂了。

2:防止重複載入同一個class

從圖二:雙親委派機制流程圖中,我們可以看出,委託向上問一問,如果載入過,就不用再載入了。

雙親委派機制簡單理解

簡單一句話:我爸是李剛,有事找我爸。簡單三個字:往上捅

雙親委派就是,有啥事,先問問老爹,如果老爹不行,再問問爺爺,如果爺爺也沒有,再告爸爸,爸爸再告訴訴兒子,你自己看著辦吧。

為什麼要往上捅呢?是因為沙箱安全機制

四種類載入機制的管轄範圍

一:啟動類載入器(Bootstrap ClassLoader):

是由c++編寫的,是JVM自身的一部分。用來載入Java核心類庫的(java。*)的。構造ExtClassLoader和AppClassLoader的。

需要注意的是:由於其是虛擬機器自身的一部分,開發者是服務直接獲取到啟動類載入器的引用的,所以是不允許直接透過引用進行操作。

這個類載入器負責存放在

\lib目錄中的,或是被-Xbootclasspath引數所指定的路徑中的,並且是虛擬機器識別的類庫載入到虛擬機器記憶體中

二:擴充套件類載入器(Extension ClassLoader):

Java語言編寫的,載入擴充套件庫。如classPath中的jre,javax。*(也即:

\lib\ext目錄中)或是java。ext。dir指定位置中的類。開發者可以直接使用這個擴充套件類載入器

Java語言編寫的,這個載入器是由sun。misc。Launcher$ExtClassLoader來實現的

三:應用程式類載入器(Application ClassLoader)

這個類載入器是由sun。misc。Launcher#AppClassLoader實現的。由於這個類載入器是ClassLoader中的getSystemClassLoader()方法的返回值。所以也稱為系統載入器。

賦值載入使用者類路徑(ClassPath)上所指定的類庫。

四:使用者自定義類載入器(CustomClassLoader)

Java語言編寫的,使用者自定義類載入器,可以載入指定路徑的class檔案