在java源文件(.java)被編譯成字節碼(.class)文件後,啓用命令java Demo,就會調用Demo類的main()方法來啓動java虛擬機(jvm)。當JVM加載到內存後,調用Demo的main()方法開始它的工作。JVM將按特定順序做三件事:加載、鏈接和初始化。
1. 加載
JVM將java類的二進制形式加載到內存中,並將他緩存在內存中,以便後面使用,如果沒有找到指定的類就會拋出異常classNotFound,進程在這裏結束。沒有錯誤就繼續在Java堆中生成一個代表這個類的java.lang.Class對象,作爲方法區域數據的訪問入口。
2.鏈接
這個階段做三件事:驗證、準備和解析(可選)。驗證是JVM根據java語言和JVM的語義要求檢查這個二進制形式。例如,如果篡改經過編譯後的類文件,那麼這個類文件可能就不能使用了。準備是指準備要執行的指定的類,準備階段爲變量分配內存並設置靜態變量的初始化。在這個階段分配的僅爲類的變量(static修飾的變量),而不包括類的實例變量。對非final的變量,JVM會將其設置成“零值”,而不是其賦值語句的值:如
public static int num = 8;
那麼在這個階段,num的值爲0,而不是8。 final修飾的類變量將會賦值成真實的值。
解析是檢查指定的類是否引用了其他的類/接口,是否能找到和加載其他的類/接口。這些檢查將針對被引用的類/接口遞歸進行,JVM的實施也可以在後面階段執行解析,即正在執行的代碼真正要使用被引用的類/接口的時候。 例如:指定的類包含下面代碼:
MathUtil.add(2,1);
那麼在調用靜態add方法之前,JVM將加載、鏈接、初始化MathUtil類。
3.初始化
最後一步中,JVM用賦值或者缺省值將靜態變量初始化,初始化發生在執行main方法之前。在指定的類初始化前,會先初始化它的父類,此外,在初始化父類時,父類的父類也要這樣初始化。這個過程是遞歸進行的。
public class StaticInitTest {
public static int a=5;
public static int b=a*2;
static{
System.out.println("Static");
System.out.println(b);
}
public static void main(String[] args) {
System.out.println("main method");
}
}
運行後輸出:
Static
10 //在執行main方法前先將靜態變量初始化,並執行靜態初始化程序
main method //之後執行main方法
最後使用和卸載就不說了,使用過程就是根據程序定義的行爲執行,卸載由GC完成。