1. 程式人生 > >JVM 之類載入器

JVM 之類載入器

一、什麼是 JVM

  JVM(Java Virtual Machine)是一個可以執行 Java 位元組碼檔案(即 .class 檔案)的虛擬機器程序。當 Java 原始檔能被成功編譯成 .class 檔案,就能在不同平臺上的不同版本的 JVM 執行,因為 JVM 能將相同的 .class 檔案解釋稱不同平臺的機器碼。正是因為 JVM 的存在,Java 被稱為與平臺無關的語言。

  一般而言,.java 檔案經過編譯後會得到 .class 檔案,而將這個檔案載入到記憶體之前需要先通過類載入器,先簡單過一下圖:

            

二、類載入過程

  類載入的過程為: 載入-->連線(驗證-->準備-->解析)-->初始化。下面介紹其中的幾個過程。

 1、載入

  這個過程主要是通過類的全限定名,例如 java.lang.String 這樣帶上包路徑的類名,獲取到位元組碼檔案;然後將這個位元組碼檔案代表的靜態儲存結構(可簡單理解為物件建立的模板)存在方法區,並在堆中生成一個代表此類的 Class 型別的物件,作為訪問方法區中“模板”的入口,往後建立物件的時候就按照這個模板建立。

  舉個例子,有時候通過反射建立物件,像當初學 JDBC 時會通過 Class.getName("com.mysql.jdbc.Driver.class").newInstance() 建立物件,通過 Class 和相應的全限定類名獲取到方法區中的“模板”然後建立物件。

        

 2、驗證

  驗證過程主要確保被載入的類的正確性。首先要先驗證檔案格式是否規範,如果只是通過 .class 字尾來辨別,那隨便把字尾名改一下就可以跑程式了,那豈不是很容易出事。來看看位元組碼檔案大概是長什麼樣的:

       

 

  注意看字首 cafe babe(咖啡寶貝?)這只是驗證的其中一個點,還會驗證位元組碼檔案裡是否包含主次版本號等驗證資訊。

 3、準備

  這個階段主要是給類變數(靜態變數)分配方法區的記憶體並初始化。例項變數不是在這個階段分配記憶體,例項變數是隨著物件一起分配在堆中。另外,給靜態變數初始化為零值或空值,比如public static int n=5;這裡並不是馬上給 n 這個變數賦值為 5,而是先將其賦值為 0,類似的,如果是引用資料型別,則預設為 null。還有一點需要注意的是,對於 final 型別的資料,必須在程式內給它賦值,系統不會自動初始化,例如 static String str = "hello" + “world”;String 是 final 型別的,在編譯階段就給它優化成 static String str = "helloworld” ,並且將 "helloworld" 放進了常量池。

 4、初始化

  這個階段就是將靜態變數賦值為初始值,還是 public static int n=5; 這回給 n 賦值為 5 了。

三、類載入器

        

  啟動類載入器是由C/C++寫的,主要負責載入 jre\lib 目錄下的類;擴充套件類載入器主要負責載入 jre\lib\ext 目錄下的類;而應用程式類載入器主要負責載入我們自己編寫的類;當然還能自己寫類載入器,即自定義載入器。程式主要由前面三個類載入器相互配合載入的。

public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        System.out.println(main.getClass().getClassLoader());
        System.out.println(main.getClass().getClassLoader().getParent());
        System.out.println(main.getClass().getClassLoader().getParent().getParent());
    }
}  

  由於啟動類載入器是 C/C++ 語言寫的,所以輸出為 null

 雙親委派機制

  在類載入的過程中,存在著雙親委派機制,即當要載入一個類時,先由父類載入器載入,當父類載入器沒辦法載入時,才由下面的載入器載入,來看一個程式:

package java.lang;   // 自定義的包

public class String {
    public static void main(String[] args) {
        System.out.println("這是自定義的java.lang.String類");
    }
}

  

  由於 jre\lib\ext 中存在 java.lang.String 類,當載入該類的時候,根據全限定名進行查詢,找到後由啟動類載入器載入,發現 String 類中不包含 main() 方法,因此程式出錯。&nb