本篇文章主要是摘抄周志明的《深刻理解java虛擬機》這本書
類加載而後被使用最後被卸載,整個聲明週期包括以下: 加載,鏈接,初始化,使用,卸載。且鏈接中又包括 驗證,準備,解析這個過程。java
"加載"是類加載過程當中的一個階段,在加載階段,虛擬機須要完成如下三個事情數據結構
加載階段完成之後虛擬機外部的字節流就按照虛擬機所需的格式存儲在 方法區之中,方法區中的數據存儲格式由虛擬機實現 自行定義,虛擬機未規定此區域的具體數據機構。而後在內存中實例化一個java.lang.Class類的對象(並無規定在java堆中,對於HotSpot 虛擬機而言,Class對象比較特殊,雖然它也是一個對象,可是它存放在方法區中)這個對象將做爲程序訪問方法區中這些類型數據的外部接口。佈局
文件格式校驗編碼
第一輪的驗證遠非這些,上面驗證只是HotSpot中的驗證的一小部分,這部分主要是基於字節流進行驗證的,保證字節流能正確的解析並存儲在方法區以內 ,格式上符合描述一個java類型信息的要求。只有經過這個階段的驗證,字節流纔會進入內存的方法區中進行存儲,因此後面的三個階段所有是基於方法區 的存儲結構進行的,不會再直接操做字節流。翻譯
元數據校驗
這一階段是對字節碼描述的信息語義分析,保證其描述的信息符合java語言規範的要求,可能包括的驗證點以下:指針
字節碼驗證
這個階段是更爲複雜和嚴格的驗證,經過對數據流和控制流的分析,肯定語義是合法的,符合邏輯的。這一階段對類的方法體進行校驗分析,保證被校驗類的方法 在運行時不會產生對虛擬機有害的操做,例如:code
符號引用驗證 最後一階段的校驗發生在虛擬機將符號引用轉化爲直接引用的時候,這個轉化動做將在鏈接的第三個階段,解析階段中發生。符號引用驗證的目的是確保解析動做能 正常的執行,若是沒法經過符號引用驗證,將會拋出java.lang.NoSuchMethodError,java.lang.NoSuchFieldError,java.lang.IllegalAccessError等 一般會校驗如下內容:對象
對於虛擬機類加載機制來講,驗證階段是一個很是重要的階段,可是不是一個必須的階段,覺得對程序運行期沒有影響。若是所運行的代碼是很是可信的,已經被反覆的校驗過,能夠考慮使用 -Xverify:none 參數來關閉大部分的類檢驗過程,縮短虛擬機的編譯時間繼承
準備階段是正式爲類變量分配內存地址並設置類變量初始值的階段,這些變量所使用的初始值都將在內存中分配。可是須要注意的是,這時候進行內存分配的僅包括類變量(被static修飾的變量), 並不包括實例變量,實例變量將會在對象實例化的時候隨着對象一塊兒分配在Java堆中。其次,這裏所說的初始值一般狀況下是數據類型的零值,假設有以下定義:索引
public static int value=123;
那變量value在準備階段事後的初始值是0,而不是123,由於這時候還沒有開始執行任何java方法,而把value賦值爲123的putstatic指令是在程序編譯以後,存放於類構造器<clinit>()方法之中,因此 把value賦值爲123的動做將在初始化階段纔會執行。
可是上面所說的一般狀況會把value值設置爲0值,可是若是按照下面定義:
public static final int value=123
這時候再準備階段value值就會被初始化爲Constant-Value屬性所值的值,value就會被賦值爲123
解析階段是虛擬機將常量池中的符號引用替換爲直接引用的過程
類初始化階段是類加載過程的最後一步,前面的類加載過程當中,除了在加載階段用戶引用程序能夠經過自定義類加載器參與以外,其他動做徹底由虛擬機主導和控制。到了初始化階段才真正開始執行類中定義的java代碼
在準備階段,變量已經賦值過一次初始值,而在初始化階段,則會根據程序初始化類變量和他資源。或者說,初始化階段是執行類構造器<clinit>()方法的過程
<clinit>()方法石油編譯器自動收集類中的全部變量的賦值動做和靜態語句塊(static{}塊)中的語句合併產生的, 編譯器收集的順序是由語句在源文件中出現的順序所決定的,靜態語句塊中只能訪問到定義在靜態語句以前的變量, 定義在它以後的變量,在前面靜態語句塊能夠賦值,可是不能訪問。
public static Test{ static( i = 0;//給變量賦值能夠正常的編譯經過 System.out.print(i); //這句編譯器會提示「非法向前引用」 ) static int i=1; }