JVM載入class檔案的原理機制
Java語言是一種具有動態性的解釋型語言,類(class)只有被載入到JVM中後才能執行。當執行指定程式時,JVM會將編譯生成的.class檔案按照需求和一定的規則載入到記憶體中,並組織成為一個完整的Java應用程式。這個載入過程是由類載入器來完成的,具體來說,就是由ClassLoader和它的子類來實現的。類載入器本身也是一個類,其實質是把類檔案從硬碟讀取到記憶體中。
類的載入方式分為隱式載入與顯式載入兩種。隱式載入指的是程式在使用new等方法建立物件時,會隱式地呼叫類的載入器把對應的類載入到JVM中。顯式載入指的是通過直接呼叫class.forName()方法來把所需要的類載入到JVM中。
(任何一個工程專案都是由許多個類組成的,當程式啟動時,只把需要載入的類載入到JVM中,其他類只有被使用到的時候才會被載入,採用這種方法,一方面可以加快載入速度,另外一方面可以節約程式執行過程中對記憶體的開銷。此外,在Java語言中,每個類或介面都對應一個.class檔案,這些檔案可以被看成一個個可以被動態載入的單元,因此當只有部分類被修改時,只需要重新編譯變化的類即可,而不需要重新編譯所有檔案,因此加快了編譯速度。)
在Java語言中,類的載入是動態的,它並不會一次性將所有的類全部載入後再執行,而是保證程式執行的基礎類(例如基類)完全載入到JVM中,至於其他類,則在需要時才載入,節省了記憶體開銷。
在Java語言中,可以把類分為3類:系統類、擴充套件類和自定義類。Java針對這3種不同的類提供了3中型別的載入器,這3種載入器的關係如下:Java的類載入器有三個,對應Java的三種類:
Bootstrap Loader // 負責載入系統類 (指的是內建類,像是String,對應於C#中的System類和C/C++標準庫中的類) | - - ExtClassLoader // 負責載入擴充套件類(就是繼承類和實現類) | - - AppClassLoader // 負責載入應用類(程式設計師自定義的類)
以上這三個類是如何協調工作來完成類的載入呢?
其實,它們是通過委託的方式實現的。具體而言,就是當有類需要被載入時,類載入器會請求父類來完成這個載入工作,父類會使用其自己的搜尋路徑來搜尋需要被載入的類,如果搜尋不到,才會由子類按照其搜尋路徑來搜尋待載入的類。下例可以充分說明類載入器的工作原理:
package com.js;
/**
* 說明類載入器的工作原理
* @author jiangshuai
*
*/
public class TestLoader {
public static void main(String[] args){
//呼叫Class載入器
ClassLoader clApp = TestLoader.class.getClassLoader();
System.out.println(clApp);
//呼叫上一層Class載入器
ClassLoader clExt = clApp.getParent();
System.out.println(clExt);
//呼叫根部Class載入器
ClassLoader clBoot = clExt.getParent();
System.out.println(clBoot);
}
}
執行結果:
null
從上例可以看出,TestLoader類是由AppClassLoader來載入的。另外需要說明的一點是,由於Bootstrap Loader是用C++語言來實現的,因此,在Java語言中是看不到它的,所以此時程式會輸出null。
類載入的主要步驟分為以下3步:
1.裝載:查詢和匯入class檔案;
2.連線:
(1)檢查:檢查載入的class檔案資料的正確性;
(2)準備:為類的靜態變數分配儲存空間;
(3)解析:將符號引用轉換成直接引用(這一步是可選的)
3.初始化:初始化靜態變數,靜態程式碼塊。
這樣的過程在程式呼叫類的靜態成員的時候開始執行,所以靜態方法main()才會成為一般程式的入口方法。類的構造器也會引發該動作。