天天看點

深入了解java虛拟機-4 虛拟機類加載機制

在java語言中,類的加載,連結和初始化都是在運作期間發生的,是以java語言天生就可以實作動态拓展

1.類的生命周期

加載->驗證->準備->解析->初始化->使用->解除安裝 驗證+準備+解析 叫做連接配接(Linking) 類的生命周期共有7步,其中加載,驗證,準備,使用,解除安裝的順序是固定的,但是解析和初始化不是固定的,某些情況下解析可以在初始化後面進行

1.加載

加載過程中需要完成三件事情: 1.通過類的全限定名來擷取此類的二進制位元組流 2.将這個位元組流代表的靜态存儲結構轉化為方法區的運作時資料結構 3.在記憶體中生成java.lang.class對象,作為方法區這個類各種資料的通路入口(Class對象雖然是對象但是存在于方法區中) 加載過程中可以使用自己定義的加載器來控制位元組流

2.驗證

雖然通過java源碼編譯出來的位元組流是相對安全的,但是也有可能位元組流并不是通過源碼編譯出來的,而是經過惡意修改的,可能會使系統崩潰,是以java虛拟機要進行驗證 驗證過程有三步: 1.檔案格式驗證 2.中繼資料驗證 3.位元組碼驗證 4.符号引用驗證

3.準備

準備階段是正式為類變量配置設定記憶體并且設定類變量初始值的階段 注意: 1.這裡僅對類變量,static修飾的變量,而不是執行個體變量初始化. 2.這裡的初始值是通常意義下的零值,如 public static Int value = 5; 經過準備過,value的值其實為0 3.特殊情況下:如public static final Internet value = 5;就會被設定為5;

4.解析

解析過程就是虛拟機将常量池裡面的符号引用轉換為直接引用, 虛拟機沒有要求解析的時間,隻要求了在 new .instanceof, getstatic等16個用于操作符号引用的位元組碼指令前解析 1.除invokedynamic外,對第一次解析的結果進行緩存(在運作時常量池中記錄直接引用,并且将常量标記為已解析狀态) 2.對于invokedynamic指令,他的引用為動态調用點限定符,等到執行時,才開始解析

常量池中有七種常量類型,下面有七種解析過程:

.....

5.初始化

java虛拟機規格規定了5種情況下,必須對類進行初始化 1.遇到new,getstatic,putstatic,或者invokestatic時,如果類沒有進行初始化,則必須進行初始化 通常在下面情況下初始化:new一個執行個體時,調用類的靜态方法,或者靜态字段時(final标記的除外,他會在編譯器就将結果放入常量池中) 2.使用java.util.reflect對類進行反射時 3.初始化一個執行個體時,如果父類還未初始化,先觸發父類初始化 4.虛拟機啟動,需要一個執行的主類(含有main方法的那一個),必須先初始化這個主類 5.使用jdk1.7的動态語言支援時,一個java.lang.invoke.MethodHandle執行個體最後解析結果是getstatic,putstatic.invokestati句柄,這個對應的類要先初始化

例子

package day20150908;

public class LoadTest {

	static{
		System.out.println("LoadTest init");
	}
	public static void main(String[] args) {
		System.out.println(SubClass.value);
	}
}

class SuperClass{
	static{
		System.out.println("SuperClass init~");
	}
	public static int value =1; 
}

class SubClass extends SuperClass{
	static{
		System.out.println("SubClass init~");
	}
}
           

運作結果: LoadTest init

SuperClass init~

1

main方法被執行前,先初始化loadtest類 因為隻調用了父類的靜态變量是以隻需要初始化父類,并不需要初始化子類

類初始化是類加載過程的最後一步,前面的類加載過程中,處理加載階段可以使用自己的加載器,其他都是虛拟機完成的,到了初始化這一步才是引用java程式代碼 初始化過程是執行類構造器<Clint>()方法的過程