深入java虛擬機器(二) 類的載入機制/生命週期
一、類的載入時機
類從被載入到虛擬機器到解除安裝為止,整個生命週期包括:載入、校驗、準備、解析、初始化、使用、解除安裝這幾個過程,其中校驗、準備、解析這三個過程又稱為初始化。嚴格意義上講這幾個過程並不是按部就班的進行,也會有相互交叉進行,尤其是校驗過程,也可能是在初始化階段之後再開始。
載入
在載入階段,虛擬機器要完成3件事情:
1、通過類的全名來獲取此類的二進位制位元組流
2、將位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構
3、在記憶體中生成一個代表這個類的java.lang.Class的物件,作為方法區的這個類的各種資料的訪問介面
1、從包中讀取
2、從網路中讀取,如Applet
3、執行時計算生成,入動態代理技術,java.lang.reflect.Proxy,也就是反射的方式
4、由jsp生成對應Class類
5、從資料庫中讀取(很少見),如中介軟體伺服器可以把程式安裝在資料庫中來完成程式程式碼在叢集間轉發
陣列類不通過類載入器建立,而是通過虛擬機器直接建立
1、如果陣列類是自定義引用型陣列類,則遞迴載入過程去載入這個元件型別,該陣列將在載入該元件型別的類載入器的類名空間上被標識
2、如果是基本型別陣列(int[]),虛擬機器則會把陣列與引導類載入器關聯
3、如果是基本型別陣列,陣列的可見性將預設是public
載入完成以後,二進位制位元組流儲存在方法區,然後在記憶體中例項化一個java.lang.Class類的物件(一般在堆中,特殊的虛擬機器HotSpor存在在方法區中)
驗證
驗證的目的是保證Class檔案的位元組流包含的資訊符合虛擬機器的要求
大致上分為4個驗證:檔案格式驗證、元資料驗證、位元組碼驗證、符號引用驗證
1、檔案格式驗證:主要是驗證位元組流是否符合Class檔案格式的規範,並且能被當前版本的虛擬機器處理
2、元資料驗證:對位元組碼描述的資訊進行語義分析,以保證其描述的資訊符合java的語言規範的要求
3、位元組碼驗證:是驗證中最複雜的,主要目的是通過資料流和控制流分析,確定程式時候合法、符合邏輯。在元資料資訊中的資料型別驗證完後,這個階段對類的方 法體進行驗證分析。
4、符號引用驗證:這個階段驗證發生在虛擬機器將符號引用轉化為直接引用的時候,這個轉化動作在解析階段中發生。
準備
準備階段是為類變數分配記憶體並設定初始值(這裡說的類變數,也就是靜態變數,對應的例項變數是隨物件例項化分配在堆記憶體中),這些記憶體都在方法區中進 行。
注意:靜態變數在準備階段時是預設值,比如static int a = 123,在準備階段還是賦值0的,初始化階段才會賦值123。
如果是常量,也就是final修飾了之後,public static final int a = 123,準備階段還是賦值為123。
解析
解析階段是將常量池的符號引用替換為直接引用的過程
符號引用:指向物件例項的記憶體地址的引用
直接引用:物件例項的記憶體地址
初始化
對於初始化,虛擬機器嚴格定義了5種初始化的條件
1、new關鍵字、get方法呼叫靜態變數、set方法修改靜態變數、呼叫靜態方法
2、使用反射方法對類進行呼叫時
3、子類初始化時檢查父類是否初始化,先觸發父類初始化
4、執行主類先初始化(通常所說的main方法)
5、當使用jdk1.7的動態語言支援時,如果有一個java.lang.invoke.MethodHandle例項最後的解析結果REF_getstatic、REF_setstatic、REF_invokestatic的方法句 柄,並且這個方法控制代碼所對應的類沒有進行過初始化,則需要先觸發其初始化
只有這5中場景的行為稱為對一個類進行主動引用。在類的初始化階段,只會初始化類的靜態變數的賦值語句和靜態語句,非靜態的變數和語句在例項化物件的時候才 會初始化。
除了上述5中情況,其他的所有引用類的方式都不會觸發初始化,稱為被動引用。
1、引用父類的靜態欄位,只會引起父類的初始化,而不會引起子類的初始化。
2、定義類陣列,不會引起類的初始化。
3、引用類的常量,不會引起類的初始化。
解除安裝
1、該類所有的例項都已經被回收,也就是java堆中不存在該類的任何例項。
2、載入該類的ClassLoader已經被回收
3、該類對應的java.lang.Class物件沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法
這時候,虛擬機器會解除安裝類,並進行垃圾回收