一個類從加載到虛擬機到使用結束從虛擬機卸載包括了加載、驗證、準備、解析、初始化、使用、卸載,即爲一個類的生命週期java
下面來看一下類加載的過程,即加載、驗證、準備、解析、初始化5個階段都作了什麼事:安全
加載階段虛擬機主要3件事:
驗證階段大致上會完成4個階段的驗證(文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證),以保證虛擬機中類的規範和安全。
文件格式驗證,校驗字節流是否複合Class文件的格式:數據結構
0xCAFEBABE
(十六進制class文件中的前4個字節)開頭;元數據類型,校驗語義是否符合Java語言規範的要求:多線程
字節碼驗證,校驗類的方法體,肯定語義是否符合邏輯:jsp
準備階段是爲類變量分配內存並設置類變量初始值的階段
這裏所說的初始值並非指代碼賦的值,而是數據類型的默認值,如public static int value = 123;
在準備階段事後,value會被置爲0,而不是123。
同時要注意,public static final int value = 123;
這種使用final修飾的變量,在準備階段就會被賦值爲123,而不是初始值。spa
解析階段會將常量池內的符號引用轉換爲直接引用,關於符號引用和直接引用的解釋以下:
public class Test{ public void foo(){ int a = Intf.intValue; } } class Intf{ public static int intValue = 123; }
編譯代碼以後咱們用javap -verbose Test
來查看class文件中的內容:線程
Constant pool: #1 = Methodref #4.#12 // java/lang/Object."<init>":()V #2 = Fieldref #13.#14 // Intf.intValue:I #3 = Class #15 // Test #4 = Class #16 // java/lang/Object // 省略部分代碼... public void foo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=1 0: getstatic #2 // Field Intf.intValue:I 3: istore_1 4: return LineNumberTable: line 3: 0 line 4:
能夠看到常量池第2項是一個符號引用,指向了Intf.intValue
初始化階段是類加載的最後一個階段,主要執行類的<clinit>方法(不一樣與<init>方法,<init>方法是在顯式調用constructor時執行,而<clinit>方法在初始化階段就會執行),<clinit>()方法會執行賦值操做和執行靜態語句快中的內容,換句話說,若是代碼中沒有靜態語句塊和賦值操做,那麼就能夠沒有<clinit>()方法。
這個階段虛擬機會保證父類的<clinit>()方法會在子類的<clinit>()方法前執行,並且在多線程環境中,虛擬機會保證<clinit>()方法的同步。代理
參考文獻:《深刻理解Java虛擬機》 - 周志明指針