如何理解Java類加載機制

java 類加載機制 也就是class文件到虛擬機

在這裏插入圖片描述

加載

  • 通過一個類的全限定名獲取定義此類的二進制字節流
  • 將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構
  • 在Java堆中生成一個代表這個類的java.lang.Class對象,作爲對方法區中這些數據的訪問入口
  • 注意: 這裏並不一定非得從一個class文件獲取,這裏既可以從zip包中讀取(war,jar)也可以在運行時計算生成(動態代理),也可以由其他文件生成。

鏈接

  1. 驗證:爲了確保類文件的字節流中包含的信息符合當前虛擬機的要求。
  • 文件格式驗證
  • 字節碼驗證
  • 元數據驗證
  • 引用符號驗證
  1. 準備: 爲類的靜態變量分配內存並將其初始化默認值。

準備階段是正式爲類的靜態變量分配內存,並設置類的靜態變量的初始值階段,在方法區中分配這些靜態變量所使用的內存空間。
注意: 這裏說的初始化值概念是:
比如 static int value= 88; 實際上value在準備階段過後的初始化值爲0,而不是88.
或者 static final int value = 88; 在編譯階段會爲value生成ConstactValue屬性,在準備階段虛擬機會根據ConstactValue屬性複製爲88;

  1. 解析:指虛擬機將常量池中的符號引用替換成直接引用。

符號引用與虛擬機的內存佈局無關,引用的目標並不一定要加載到內存中。在Java中,一個java類將會編譯成一個class文件。在編譯時,java類並不知道所引用的類的實際地址,因此只能使用符號引用來代替。比如org.simple.People類引用了org.simple.Language類,在編譯時People類並不知道Language類的實際內存地址,因此只能使用符號org.simple.Language(假設是這個,當然實際中是由類似於CONSTANT_Class_info的常量來表示的)來表示Language類的地址。各種虛擬機實現的內存佈局可能有所不同,但是它們能接受的符號引用都是一致的,因爲符號引用的字面量形式明確定義在Java虛擬機規範的Class文件格式中。
直接引用可以是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄,如果有了直接引用,那引用的目標已經在內存中存在了。

初始化

對類的靜態變量,靜態代碼塊執行初始化操作。

初始化階段是類加載最後一個階段,除了在加載可以自定義類的加載器以外,其他操作都是有JVM主導的,到了初始化階段才真正執行類中定義的Java代碼。
初始化階段是執行類構造方法的過程,構造方法是由編譯器自動完成類中的靜態變量賦值和靜態代碼塊執行的。虛擬機會保證構造方法執行之前,父類的構造方法已經執行完畢。
如果一個類中沒有靜態變量,也沒有靜態語句塊,那麼編譯器可以不生成構造方法。

注意:以下幾種情況不會執行類初始化

  1. 通過子類引用父類的靜態字段,只會觸發父類的初始化,而不會觸發子類的初始化。
  2. 定義了對象數組,不會觸發類的初始化。
  3. 常量在編譯期間會存入調用類的常量池中,本質上並麼有直接引用定義常量的類,不會觸發定義常量所在類的初始化。
  4. 通過類名獲取class對象,不會觸發類的初始化
  5. 通過class.forName加載指定類時,如果指定參數initialize爲false時,也不會觸發類的初始化
  6. 通過ClassLoader默認的loadclass方法,也不會觸發初始化。

類加載器 ClassLoader

在加載(Load)階段,其中第(1)步:通過類的全限定名獲取其定義的二進制字節流,需要藉助類裝載器完成,顧
名思義,就是用來裝載Class文件的。通過一個類的全限定名獲取定義此類的二進制字節流

  1. Bootstrap ClassLoader 啓動類加載器 負責加載$JAVA_HOME中 jre/lib/rt.jar 裏所有的class或Xbootclassoath 選項指定的jar包。由C++實現,不是ClassLoader子類。
  2. Extension ClassLoader 擴展類加載器 負責加載java平臺中擴展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar 或 -Djava.ext.dirs指定目錄下的jar包。
  3. App ClassLoader 應用程序類加載器 負責加載classpath中指定的jar包及 Djava.class.path 所指定目錄下的類和jar包。
  4. Custom ClassLoader 自定義類加載器 通過java.lang.ClassLoader的子類自定義加載class,屬於應用程序根據自身需要 自定義的ClassLoader,如tomcat、jboss都會根據j2ee規範自行實現ClassLoader。
    在這裏插入圖片描述
    類加載器的實現原理(雙親委派)
    當一個類加載器收到類加載任務時,會先交給其父類加載器去完成,因此最終加載任務都會傳遞到頂層的啓動類加載器。只有當父類加載器無法完成加載任務時,纔會嘗試當前類加載器去執行加載任務。
    檢查某個類是否已經加載:順序是自底向上,從Custom ClassLoader到BootStrap ClassLoader逐層檢查,只要某個Classloader已加載,就視爲已加載此類,保證此類只所有ClassLoader加載一次。
    加載的順序:加載的順序是自頂向下,也就是由上層來逐層嘗試加載此類
    雙親委派機制

定義:如果一個類加載器在接到加載類的請求時,它首先不會自己嘗試去加載這個類,而是把這個請求任
務委託給父類加載器去完成,依次遞歸,如果父類加載器可以完成類加載任務,就成功返回;只有父類加
載器無法完成此加載任務時,才自己去加載。
優勢: Java類隨着加載它的類加載器一起具備了一種帶有優先級的層次關係。比如,Java中的Object類,
它存放在rt.jar之中,無論哪一個類加載器要加載這個類,最終都是委派給處於模型最頂端的啓動類加載器
進行加載,因此Object在各種類加載環境中都是同一個類。如果不採用雙親委派模型,那麼由各個類加載
器自己取加載的話,那麼系統中會存在多種不同的Object類。
破壞: 重寫loadClass()方法。線程上下文類加載器破壞了雙親委派模型的一般性原則。

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