1. 程式人生 > >JVM——深入解析原理和執行機制(一)類載入器

JVM——深入解析原理和執行機制(一)類載入器

      上次我們說了一下jvm中類載入的過程,大概有載入,連線(驗證,準備,解析),初始化這麼幾個步驟,當然要實現這些功能就需要有載入器,今天我們就來說說jvm中的類載入器。

一、分類

      系統要想執行一個命令,就需要把所需要的資源載入到記憶體,然後再使用。java也是一樣的,這就有了類載入器。類載入器主要分為啟動類載入器(BootstrapClassLoader)、擴充套件類載入器(ExtensionClassLoader)和系統類載入器(SystemClassLoader ),另外還有上下文類載入器(ContextClassLoader)和自定義的類載入器。

1、BootstrapClassLoader

      啟動類載入器引導類裝入器是用原生代碼實現的類裝入器,它負責將 /lib下面的核心類庫或-Xbootclasspath選項指定的jar包載入到記憶體中。由於引導類載入器涉及到虛擬機器本地實現細節,開發者無法直接獲取到啟動類載入器的引用,所以不允許直接通過引用進行操作。

      啟動類載入器是最低層的載入器,它是由C++編寫的,因為載入器是一個類,也需要通過類載入器載入,啟動類載入器就能完成這個功能。

2、ExtensionClassLoader

      擴充套件類載入器是由ExtClassLoader類實現的。它負責將< Java_Runtime_Home >/lib/ext或者由系統變數-Djava.ext.dir指定位置中的類庫載入到記憶體中。開發者可以直接使用標準擴充套件類載入器。

這裡寫圖片描述

3、SystemClassLoader

      系統類載入器也成為應用程式類載入器,是由AppClassLoader類實現。它負責將系統類路徑java -classpath或-Djava.class.path變數所指的目錄下的類庫載入到記憶體中。開發者可以直接使用系統類載入器。

這裡寫圖片描述

4、ContextClassLoader

      執行緒上下文類載入器(context class loader)是從 JDK 1.2 開始引入的。類 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設定執行緒的上下文類載入器。如果沒有通過 setContextClassLoader(ClassLoader cl)方法進行設定的話,執行緒將繼承其父執行緒的上下文類載入器。Java 應用執行的初始執行緒的上下文類載入器是系統類載入器。
      而執行緒上下文類載入器破壞了“雙親委派模型”,可以在執行執行緒中拋棄雙親委派載入鏈模式,使程式可以逆向使用類載入器。

      Java 提供了很多服務提供者介面(Service Provider Interface,SPI),允許第三方為這些介面提供實現。常見的 SPI 有 JDBC、JCE、JNDI、JAXP 和 JBI 等。

      這些 SPI 的介面由 Java 核心庫來提供,而這些 SPI 的實現程式碼則是作為 Java 應用所依賴的 jar 包被包含進類路徑(CLASSPATH)裡。SPI介面中的程式碼經常需要載入具體的實現類。那麼問題來了,SPI的介面是Java核心庫的一部分,是由啟動類載入器(Bootstrap Classloader)來載入的;SPI的實現類是由系統類載入器(System ClassLoader)來載入的。引導類載入器是無法找到 SPI 的實現類的,因為依照雙親委派模型,BootstrapClassloader無法委派AppClassLoader來載入類。

      由此可見,執行緒上下文類載入器解決了SPI的類載入問題。

5、CustomClassLoader

      開發人員可以通過繼承java.lang.ClassLoader類的方式實現自己的類載入器,以滿足一些特殊的需求。
      java.lang.ClassLoader 基本職責就是根據一個指定的類的名稱,找到或者生成其對應的位元組程式碼,然後從這些位元組程式碼中定義出一個Java類,即java.lang.Class類的一個例項。 除此之外,ClassLoader還負責載入Java應用所需的資源,如影象檔案和配置檔案等。

二、時機和機制

1、載入時機

  • 命令列啟動應用時候由JVM初始化載入
  • 通過Class.forName()方法動態載入
  • 通過ClassLoader.loadClass()方法動態載入

2、機制

  • 全盤負責

      當一個類載入器負責載入某個Class時,該Class所依賴的和引用的其他Class也將由該類載入器負責載入,除非顯示使用另外一個類載入器來載入

  • 父類委託

      先讓父類載入器試圖載入該類,只有在父類載入器無法載入該類時才嘗試從自己的類路徑中載入該類

  • 快取機制

      快取機制將會保證所有載入過的Class都會被快取,當程式中需要使用某個Class時,類載入器先從快取區尋找該Class,只有快取區不存在,系統才會讀取該類對應的二進位制資料,並將其轉換成Class物件,存入快取區。這就是為什麼修改了Class後,必須重啟JVM,程式的修改才會生效

三、雙親委派

      雙親委派模型的工作流程是:如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把請求委託給父載入器去完成,依次向上,因此,所有的類載入請求最終都應該被傳遞到頂層的啟動類載入器中,只有當父載入器在它的搜尋範圍中沒有找到所需的類時,即無法完成該載入,子載入器才會嘗試自己去載入該類。

這裡寫圖片描述

      1、當AppClassLoader載入一個class時,它首先不會自己去嘗試載入這個類,而是把類載入請求委派給父類載入器ExtClassLoader去完成。

      2、當ExtClassLoader載入一個class時,它首先也不會自己去嘗試載入這個類,而是把類載入請求委派給BootStrapClassLoader去完成。

      3、如果BootStrapClassLoader載入失敗(例如在$JAVA_HOME/jre/lib裡未查詢到該class),會使用ExtClassLoader來嘗試載入;

      4、若ExtClassLoader也載入失敗,則會使用AppClassLoader來載入,如果AppClassLoader也載入失敗,則會報出異常ClassNotFoundException。

總結:

      今天我們瞭解了一下類載入器的一些基礎知識,知道雙親委派模型的實現,還有一點要注意的是java提供的上下文類載入器打破了這種雙親委派的模型,具體實現的方式不再多說,大家最好看看SPI的原始碼實現,理解了這個就懂的了類載入機制。