深入理解java虛擬機——類加載機制

分類:編程 時間:2017-02-21

Java語言中,一切都可以看成是對象,而每個對象,都是一個類的在運行時表現出種種屬性的實例。那麽類,如何工作呢?

加載,鏈接,初始化

一個類,從class文件形式,到可以被使用,共經歷加載,鏈接,初始化這三個過程,其中:

  • 加載: jvm根據一個字符串的名字查找類或接口類型的二進制表示,並由此二進制表示創建類或接口的過程
  • 鏈接: 為了讓類或接口可以被jvm執行,而將類或接口並入jvm運行時狀態的過程
  • 初始化: 執行類或接口初始化方法的過程(此方法非java類的構造方法)

Java虛擬機的啟動與運行時常量池

在描述類的加載鏈接初始化之前,首先需要明確兩個概念。首先,關於Java類在程序員使用前的整個創建準備過程,是由Java的類加載機制負責完成,類加載機制運作的核心是類加載器(Class Loader),Java虛擬機的啟動便是通過引導類加載器創建一個初始類完成的,緊接著Java虛擬機鏈接這個初始類,調用它的main方法,在main方法中,虛擬機可以執行指令來鏈接另外的一些類(可能是用戶編寫的類);其次,每個class文件中都會有一個常量池表(一種數據結構),在Java虛擬機的方法區中,會保存著與之對應的運行時常量池,常量池中的所有引用為構建該類所有組成部分的符號引用。

加載

類的加載是指Java虛擬機通過類加載器(class loader)將class文件中的二進制數據讀入到內存中,並將其放入運行時數據區的方法區(運行時常量池)中,同時Java虛擬機會在運行時數據區的堆中創建一個與該類對應的Class對象,用於封裝該類在方法區中的數據結構,該Class對象為程序員提供了訪問該類各項屬性及方法的接口。

Java虛擬機提供兩種類型的類加載器:

Java虛擬機自帶的類加載器:
1.引導類加載器(由C++語言編寫,為Java虛擬機的組成部分,程序員無法訪問。啟動類加載器對應的加載 路徑為jre\lib目錄,java標準庫(大部分在rt.jar裏)位於該路徑)
2.擴展類加載器(Java語言編寫,URLClassLoader類的實例。擴展類加載器的默認路徑為jre\lib\ext目錄,使用Java擴展機制的類位於該路徑)
3.系統類加載器(Java語言編寫,URLClassLoader類的實例。父加載器是擴展類加載器,系統類加載器從classpath路徑下加載類庫,它是用戶自定義類加載器的默認父加載器)

父加載器跟子加載器並不一定是繼承關系

用戶自定義的類加載器:
Java虛擬機規範要求用戶自定義的類加載器必須繼承自ClassLoader類,同時,用戶可以定制類的加載方式。

關於類加載器加載class文件的方式,有如下幾種:

  • 從本地系統加載class文件
  • 從網絡下載class文件
  • 從jar,zip等歸檔文件中加載class文件
  • 將java文件動態編譯成class文件
  • 從專有數據庫中提取class文件(流)

Java虛擬機規範允許類加載器在預料到某個類將要被使用的時候預先加載它,在加載該類的過程當中,如果遇到class文件缺失或者存在錯誤的情況,則類加載器應在程序首次主動使用該類的時候報告LinkageError錯誤,如果程序一直沒有主動使用該類,類加載器則不報錯。

鏈接

當類被加載後,便進入鏈接階段。鏈接就是將已讀入內存的二進制數據並入Java虛擬機的運行時環境中去,鏈接共分三個步驟:

驗證:

驗證的內容包括:

  • 類文件的結構檢查:確保類文件遵從Java類文件的格式規範
  • Java語義檢查:確保類本身符合Java語言的語法規定,如abstract,final等關鍵字的特殊性
  • 字節碼驗證:確保字節碼會被虛擬機正確地執行,檢查每個操作碼及其對應操作數是否合法
  • 二進制兼容性驗證:確保相互引用的的類之間協調一致(例如版本一致)

驗證過程中,有可能會拋出VerifyError錯誤。

準備:

準備階段的任務是創建類或接口的靜態字段,並用默認值初始化這些靜態字段,這個階段不會執行任何虛擬機字節碼指令。Jav虛擬機規範強制要求,類的準備階段必須在類的初始化階段開始之前完成。

解析:

解析階段是Java虛擬機將運行時常量池中的符號引用轉換為直接引用的過程

  • 符號引用為任意字面量,其所引用的目標不一定已經加載到內存中的
  • 直接引用為指向目標的指針、相對偏移量或能間接定位到目標的句柄,直接引用的目標必須是已經加載到內存中的

初始化

初始化階段,Java虛擬機為類變量賦予初始值(非默認值,此初始值為用戶顯式賦予的值),有兩種對類變量賦值的方式,一、在變量聲明處賦值;二、在靜態塊中賦值

類變量的賦值動作會按照類變量在文件中的由上到下位置順序進行,例如:

public class Demo{
    public static int a = 1;

    static{
        a = 2;
    }

    static{
        a = 3;
    }

    public static void main(String[] args){
        system.out.pringln("a = " + a);  //此處a值為3
    }
}

Java虛擬機規範規定,類的加載和鏈接階段必須發生在初始化階段之前,若一個類在初始化的時候還未進行加載和鏈接,那麽將先進行類的加載和鏈接。若一個類存在父類,那麽初始化這個類之前,要保證父類已經被初始化。

當發生以下六種情況的時候,會觸發類的初始化動作,此六種情況被稱作對類的主動使用,如下:

  • 新建實例對象
  • 訪問類變量,或對類變量賦值
  • 訪問類方法
  • 調用反射方法建類,如:Class.forName(“com.lyu.ClassDemo”)
  • 初始化一個類的子類
  • Java虛擬機啟動時作為啟動類的類(帶main方法的入口類)

Tips:當調用ClassLoader類的loadClass方法時,並不會導致類的初始化

初始化之後,被初始化類便可以被程序所使用了。

虛擬機的退出

Java虛擬機退出的條件是,某線程調用了Runtime類或System類的exit方法,或Runtime類的halt方法,並且Java安全管理器允許這次exit或halt操作。


Tags: java語言 虛擬機 二進制 程序員 字符串

文章來源:


ads
ads

相關文章
ads

相關文章

ad