虛擬機器類載入機制_類載入時機和類的生命週期

z1340954953發表於2018-04-09

類載入機制

類的生命週期

類從被載入到虛擬機器記憶體中開始,到解除安裝出記憶體為止,生命週期包括:

載入、驗證、準備、解析、初始化、使用和解除安裝7個階段。其中驗證、準備、解析3個部分統稱為連線。


載入、驗證、準備、初始化和解除安裝這5個階段的順序是確定的,類的載入過程必須按照這種順序按部就班地開始,

可以在初始化之後再解析,為了支援java語言執行時繫結

類的初始化時機

java虛擬機器規範中並沒有對載入有強制約束,但是對於類的初始化,java虛擬機器規範中嚴格規定有且只有5種情況必須進行:

1> 遇到new、getstatic、putstatic或invokestatic這4條位元組碼指令時候,如果類沒有進行初始化,則會觸發初始化,

最常見的場景:使用new 例項化物件的時候、讀取或者設定一個類的靜態欄位(被final修飾、已在編譯期間把結果放入常量池的靜態欄位除外)的時候,以及呼叫類的靜態方法的時候

2> 使用java.lang.reflect包的時候對類進行反射呼叫的時候,如果類沒有進行過初始化,則需要先觸發其初始化

3> 初始化一個類的時候,如果發現父類還沒有進行過初始化,則需要先觸發其父類的初始化

4> 當虛擬機器啟動時候,使用者需要制定一個執行的主類(包含main方法的類),這個類會優先初始化

5> 當使用JDK1.7動態語言支援時,如果一個java.lang.invoke.MethodHandle例項最後解析方法控制程式碼是:

REF_getStatic、REF_putStatic、REF_invokeStatic,並且這個控制程式碼對應的類沒有進行過初始化,需要出發初始化操作

note:

上面的5種場景中的行為成為對一個類進行主動引用,除此之外,所有引用類的方式都不會觸發初始化,稱為被動引用。

被動引用的場景

1 . 子類呼叫父類的靜態變數:只會觸發父類的初始化、不會觸發子類的初始化

class SuperClass{
	static{
		System.out.println("SuperClass init! ");
	}
	public static int value = 1;
}
class SubClass extends SuperClass{
	static{
		System.out.println("SubClass init !");
	}
}
public class Test {
	public static void main(String[] args) throws ParseException {
		System.out.println(SubClass.value);
	}
}

 輸出結果:

證明子類呼叫父類的靜態變數,不會去觸發子類的初始化

SuperClass init! 
1

如果將父類的變數改為常量,子類呼叫這個常量,也不會去觸發父類的初始化

1

2.通過陣列定義來引用類,不會觸發此類的初始化

public static void main(String[] args) throws ParseException {
		SuperClass[] test = new SuperClass[10];
	}
3. 常量在編譯階段會存入呼叫類的常量池中,本質上並沒有直接引用到定義常量的類,不會觸發類的初始化
class ConstClass{
	static{
		System.out.println("ConstClass init ");
	}
	public static final String HELLOWORLD = "hello world!";
}
public class Test {
	public static void main(String[] args) throws ParseException {
		System.out.println(ConstClass.HELLOWORLD);
	}
}

結果並不會去輸出"ConstClass Init!",因為原始碼雖然引用了常量資訊,但是在編譯期間已將常量的值hello world儲存到Test類的常量池中,以後對常量的引用都轉為對自身常量池的引用.也就是Test類的class檔案中沒有ConstClass類的符號引用入口,這兩個類在編譯成class之後就不存在任何聯絡

注意:

介面的載入過程和類載入過程有一些不同,針對介面需要做一些特殊說明:介面也有初始化過程,前面測試中使用static{}來輸出初始化資訊的,介面中不能使用static{}語句塊,但是編譯器仍然會為介面生成<clinit>()類構造器,用於初始化介面中定義的成員變數。

介面和類真正有區別的地方是:當一個類在初始化時,要求父類全部都已經初始化過了,但是一個介面在初始化時,並不要求父介面全部完成初始化,只有在真正使用到父介面(如引用介面中定義的變數)才會初始化



相關文章