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的源碼實現,理解了這個就懂的了類加載機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章