類的整個生命週期的7個階段是:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸載(Unloading)。java
類加載的全過程主要包括:加載、驗證、準備、解析、初始化這5個階段的內容。安全
加載是類加載過程的一個階段, 在加載階段JVM須要完成如下3件事情:數據結構
加載階段(準確地說,是加載階段獲取類的二進制字節流的動做)是整個類加載過程中開發人員可控性最強的,由於加載階段既可使用系統提供的引導類加載器完成,又能夠由用戶自定義的二類加載器去完成,開發人員能夠經過定義本身的類加載器區控制字節流的獲取方式。多線程
加載階段完成後,虛擬機外部的二進制字節流就按照虛擬機所需的格式存儲在方法區中,方法區中的數據存儲格式由虛擬機的實現自行定義,虛擬機規範未規定此區域的具體數據結構。而後再內存中實例化一個java.lang.Class類的對象(這個對象,並無要求必須是在Java堆中,就HotSpot而言,Class對象比較特殊,雖然是對象,可是是存放在方法區中的),這個對象將做爲程序訪問方法區中的這些類型數據的外部接口。函數
加載階段與鏈接階段的部份內容(如一部分字節碼文件格式驗證東西)是交叉進行的,可是這兩個階段的開始時間仍然保持着固定的 前後順序。佈局
驗證是鏈接階段的第一步,這一階段的目的是爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。驗證階段是很是重要的,這個階段是否嚴謹,直接決定了Java虛擬機是否能承受惡意代碼的攻擊,它大體上會完成4個階段的檢驗工做:文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證。編碼
文件格式驗證spa
這一階段主要驗證字節流是否符合Class文件格式的規範,而且能被當前版本的虛擬機處理。線程
驗證內容包括:是否以魔數0xCAFEBABE開頭,主次版本號是否在當前虛擬機處理範圍以內,常量池的常量是否有不被支持的常量類型,指向常量的各類索引值是否有指向不存在的常量或不符合類型的常量,CONSTANT_Utf8_info型的常量中是否有不符合UTF8編碼的數據,Class文件中各個部分及文件自己是否有被刪除的或附近的其餘信息等等。翻譯
元數據驗證
第二階段主要是對類的元數據信息進行語義校驗,保證不存在不符合Java語言規範的元數據信息。
驗證內容包括:當前類是否有父類(除了Object類以外,全部類都該有父類),當前類的父類是否繼承了不被容許繼承的類(被final修飾的類),若是當前類不是抽象類,是否實現了其父類或接口之中要求實現的全部方法,類中的字段、方法是否與父類產生矛盾(如覆蓋了父類的final字段等)等等。
字節碼驗證
第三階段是整個驗證過程當中最複雜的一個階段,主要目的是經過數據流和控制流分析,肯定程序語義是合法的、符合邏輯的。
驗證內容包括:保證任意時刻操做數棧的數據類型與指令代碼序列都能配合工做,例如:保證不會出如今操做棧放置了一個int類型的數據,使用時卻按long類型來加載如本地變量表中。保證跳轉指令不會跳轉到方法體覺得的字節碼指令上。保證方法體上的類型轉換是有效的,例如:能夠把一個子類對象賦值給父類數據類型,可是不能把父類對象賦值給子類數據類型。
符號引用驗證
最後一個階段的校驗發生在虛擬機將符號引用轉化爲直接引用的時候,這個轉化動做發生在解析階段。符號引用驗證能夠看作是對類自身之外的信息進行匹配校驗。
驗證內容包括:符號引用經過字符串描述的全限定明是否能找到對應的類。在指定類中是否存在符合方法的字段描述符以及簡單名稱所描述的方法和字段。符號引用中的類、字段、方法的訪問性是否能夠被當前類訪問等等。
準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些變量所使用的內存都將在方法區中進行分配。這個階段分配內存的僅僅是類變量不包括實例變量。實例變量實在對象實例化的時候分配在堆內存中的,還有就是這裏給類變量設置的初始值「一般狀況下」下是數據類型的零值,例如:
public static int value = 666;
這個變量value的值在準備階段被設置的初始值爲0而不是666,由於此時還沒有開始執行任何Java方法,而把value賦值爲666的putstatic指令是程序編譯後,存放於類構造器<clinit>()方法之中,因此把value賦值爲666的動做將在初始化階段纔會執行。
上面說到在「一般狀況」下初始值是零值,在非「一般狀況」下也就是類字段屬性中存在常量屬性的時候,那麼在準備階段類變量就會被初始化爲常量屬性所指定的值。
public static final int value = 666;
編譯時Javac將會生成常量屬性,在準備階段虛擬機就會根據常量屬性的設置將value賦值爲666;
解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程。
符號引用(Symbolic References):符號引用以一組符號來描述所引用的目標,符號能夠是任何形式的字面量,只要使用時能無歧義地定位到目標便可。符號引用與虛擬機實現的內存佈局無關,引用目標並不必定已經加載到內存中。
直接引用(Direct References):直接引用能夠是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是和虛擬機實現內存佈局相關的,同一個符號引用在不一樣虛擬機實例上翻譯出來的直接引用通常不會相同。若是有直接引用,那引用的目標一定已經在內存中存在。
解析動做主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符,這7類符號引用,分別對應於常量池的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_MethodHandle_info和CONSTANT_InvokeDynamic_info 這7中常量類型。
類初始化階段是類加載過程的最後一步,前面的類加載過程當中,除了在加載階段用戶應用程序能夠經過自定義類加載器參與以外,其他動做徹底由虛擬機主導和控制。在準備階段,變量已經賦過一次系統初始零值了,而在初始化階段,是經過程序制定的主觀計劃去初始化類變量和其餘資源,也就是執行類構造器<clinit>()方法的過程。在上一篇「類的加載時機」中已經介紹過了,有5中狀況會出發類初始化,下面介紹的是在<clinit>()方法執行過程當中一些可能會影響程序運行行爲的特色和細節。