標籤:src orm 修改 命名空間 語句 方法 其他 需要 pat
java虛擬機器中類的載入
(JVM的大致結構圖)
從發class檔案到記憶體中的類,按先後順序,需要經過載入,連結以及初始化三大步驟。
java語言的類型可分為兩大類:基本類型(primitive type)和參考型別(references type)
基本類型:是由java虛擬機器預先定義好的。
參考型別:Java將其細分為四種,類、介面、數組類和泛型參數。
由於泛型參數會在編譯過程中被擦除(泛型型別在邏輯上可以看成是多個不同的類型,實際上都是相同的基本類型。),因此Java虛擬機器實際上只有前三種。在類、介面和數組類中,數組類是由java虛擬機器直接產生的,其他兩種則有對應的位元組流。
位元組流:最常見的形式要屬由Java編譯器產生的class檔案。除此之外,我們也可以在程式內部直接產生,或者從網路中擷取位元組流。這些不同形式的位元組流,都會被載入到java虛擬機器中,稱為類或者介面。
無論是直接產生的數組類,還是載入的類,java虛擬機器都要對其進行連結和初始化。
載入
載入是指尋找位元組流,並且據此建立類的過程。對於類來說,java虛擬機器需要藉助類載入器來完成尋找位元組流的過程。
當JVM啟動時,會形成由三個類載入器組成的初始類載入器階層:
1、啟動類載入器(BootstrapClassLoader):是嵌在JVM核心中的載入器,它是由C++實現的,沒有對應的java對象,因此在java中只能用null來代替。它主要負責載入JAVA_HOME/lib下的類庫,啟動類載入器無法被應用程式直接使用。
除了啟動類載入器外,其他的類載入器都是java.lang.ClassLoader的子類,因此有對應的java對象。這些類載入器需要先由另一個類載入器,比如說啟動了載入器,載入至java虛擬機器中,方能執行類載入。
在Java9之前,啟動類載入器負責載入最為基礎,最為重要的類,比如存放在jre的lib目錄下的jar包(以及由虛擬機器參數-Xbootclasspath指定的類)。除了啟動類載入器之外,另外兩個重要的類載入器是擴充類載入起(extension class loader)和應用類載入器(application class loader),均由java核心類庫提供。
2、擴充類載入器(extension class loader):其父類是啟動類載入器,他負責載入相對次要、但又通用的類,比如存放在jre的lib/ext目錄下的jar包中的類(以及由系統變數java.ext.dirs指定的類)。
3、應用類載入器(application class loader):其父類是擴充類載入器,它負責載入用用程式下的類。(這裡的應用程式路徑,便是指虛擬機器參數-cp/-classpath、系統變數CLASSPATH所制定的路徑。)預設情況下,應用程式中包含的類是由應用類載入器載入的。
java9引入了模組系統,,並且略微更改了上述的類載入器,擴充類載入器被改名為平台類載入器(platform class loader)。Java SE 中除了少數幾個關鍵模組,比如說、java.base是由啟動類載入器載入之類,其他的模組均由平台載入器所載入。
除了java核心類庫提供的類載入器之外,我們還可以加入自訂的載入器,來實現特殊的載入方式。舉個例子,我們可以對class檔案進行加密,載入時再利用自訂的類載入最其解密。
除了載入功能之外,類載入器還提供了命名空間的作用。在java虛擬機器中,類的唯一性是由類載入器執行個體以及類的全名一同確定的。即便是通一串位元組流,經由不同的類載入器載入,也會得到兩個不同的類。在大型應用中,我們往往會藉助這一特性,來運行同一個類的不同版本。
連結
連結是指將建立成的類合并至java虛擬機器中,使之能執行的過程。它可以分為驗證、準備及解析三個階段。
驗證:確保被載入的類能夠滿足java虛擬機器的約束條件,檢驗被載入的類是否有正確的內部結構,並和其他類協調一致。
準備:為被載入類的靜態欄位分配記憶體,並設定預設初始值。
解析:將類中的位元據中的符號引用替換成直接引用(final修改的常量的替換)。
JVM並沒有要你在連結過程中完成解析,它僅規定了:如果某些位元組碼使用了符號引用,那麼在執行這些位元組碼之前,需要完成對這些符號引用的解析。
初始化
在java代碼中如果要初始化一個靜態欄位,我們可以在聲明時直接賦值,也可以在靜態代碼塊中對其賦值。
如果直接賦值的靜態欄位被final修飾,並且他的類型是基本類型或字串時,那麼該欄位便會被java編譯器標記為常量值(ConstantValue),其初始化直接被java虛擬機器完成。除此之外的直接賦值操作以及所有靜態代碼塊中的代碼,則會被java編譯器置於同一方法中,並把它命名為<clinit>。
類載入的最後一步是初始化,便是為標記為常量值的欄位賦值,以及執行<clinit>方法的過程。java虛擬機器會通過加鎖來確保類的<clinit>方法僅被執行一次。
只有當初始化完成之後,類才正式稱為可執行檔狀態。
JVM初始化一個類分為幾步:
1、假如這個類還沒有被載入和連結,程式先載入並連結這個類。
2、假如該類的直接父類還沒有被初始化,則先初始化其直接父類。
3、假如類中有初始化語句,則依次執行其初始化語句。
類的載入和初始化何時被觸發
1、當虛擬機器啟動時,初始化使用者指定的主類。(直接使用java.exe命令來運行某個主類)
2、建立類的執行個體(new方法)。
3、調用某個類的靜態方法,初始化該靜態方法所在的類。
4、訪問某個類或介面的靜態屬性,初始化該靜態屬性所在的類。
5、使用反射機制來建立某個類或介面的對應java.lang.Class對象(Class.forName("Person")),初始化這個類。
6、子類的初始化會觸發父類的初始化。
7、如果一個介面定義了default方法,那麼直接實現或間接實現該介面的類的初始化,會觸發該介面的初始化。
8、當初次使用MehodHandle執行個體時,初始化該MehodHandle指向的方法所在的類。
註:此文為極客時間鄭雨迪專欄,java虛擬機器講解及自己查資料的學習總結。鄭雨迪《深入拆解Java虛擬機器》很不錯。
java虛擬機器類