關於JVM系列的文章,都是在讀了《深刻理解java虛擬機》一書以後的讀書筆記總結。java
在JAVA語言中,類型的加載、鏈接和初始化過程都是在程序運行期間完成的,JAVA動態拓展的語言特性就是依賴於運行期動態加載和動態鏈接這個特色實現的。類從被加載到虛擬機內存中開始到卸載出內存爲止,整個生命週期以下圖所示:數組
java虛擬機規範嚴格規定了有且只有下面五種狀況必須當即對類進行「初始化」(加載、驗證、準備這三個步驟須要在此以前開始):數據結構
遇到new、getstatic、putstatic或invokestatic這四條子節碼指令的時候,若是類沒有進行過初始化,則須要先觸發其初始化。最多見的場景是:使用new關鍵字實例化對象的時候、讀取或者設置一個類的靜態字段的時候(被final修飾,已在編譯期把結果放入常量池的靜態字段除外)以及調用一個類的靜態方法的時候。函數
使用java.lang.reflect包的方法對類進行反射調用的時候,若是類沒有進行過初始化,則須要先觸發其初始化。佈局
當初始化一個類的時候,若是發現其父類尚未進行過初始化,則須要先觸發父類的初始化。(一個接口在初始化的時候,並不要求其父藉口所有都完成了初始化,只有真正使用父接口的時候纔會初始化).net
當初始化一個類的時候,用戶須要指定一個主要執行的主類,虛擬機會先初始化這個主類。翻譯
當使用jdk1.7的動態語言支持時,若是一個java.lang.invoke.methodhandle實例最後的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,而且這個方法句柄對應的類沒有進行過初始化,則須要先觸發其初始化。指針
在加載階段,虛擬機須要完成如下三件事情:code
經過一個類的權限定名來獲取定義此類的二進制字節流。cdn
將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構。
在內存中生成一個表明這個類的java.lang.class對象,做爲方法區這個類的各類數據的訪問入口。
與非數組類的加載階段不一樣,數組類自己不經過類加載器建立,它是由java虛擬機直接建立的。若是數組的組件類型時引用類型,那就遞歸的採用以上的加載過程加載這個組件類型嗎,數組將在加載該組件類型的類加載器的類名稱上被標識。若是數組的組件類型不是引用類型,java虛擬機將會把數組標記爲於引導類加載器相關聯
類加載完成之後,虛擬機外部的二進制字節流就按照虛擬機所需的格式存儲在方法區之中。
加載階段與鏈接階段的部份內容時交叉進行的,加載階段還沒有完成,鏈接階段就已經開始,可是這兩個階段的開始時間依然保持着固定的前後順序。
驗證階段是鏈接階段的第一步,會完成如下四個階段的檢驗動做。
準備階段正式爲 類變量 分配內存並賦初始值的階段,這些變量所使用的內存都將在方法區中進行分配。
解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程。
符號引用
符號引用以一組符號來描述所引用的目標,符號能夠好似任何形式的字面量,只要使用時能無歧義地定位到目標便可。符號引用與虛擬機實現的內存佈局無關,引用的目標不必定已經加載到內存中。
直接引用
直接引用能夠是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。直接引用是和虛擬機實現的內存佈局相關的,同一個符號引用在不一樣的虛擬機實例上翻譯出來的直接引用通常不會相同。若是有了直接引用,那引用的目標一定已經在內存中存在。
解析動做主要針對類或接口、字段、類方法、接口方法四類符號引用進行,分別對應於常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info四種常量類型。
字段解析
對字段進行解析時,會先在本類中查找是否包含有簡單名稱和字段描述符都與目標相匹配的字段,若是有,則查找結束;若是沒有,則會按照繼承關係從下往上遞歸搜索該類所實現的各個接口和它們的父接口,尚未,則按照繼承關係從下往上遞歸搜索其父類,直至查找結束。
類方法解析
對類方法的解析與對字段解析的搜索步驟差很少,只是多了判斷該方法所處的是類仍是接口的步驟,並且對類方法的匹配搜索,是先搜索父類,再搜索接口。
接口方法解析
與類方法解析步驟相似,只是接口不會有父類,所以,只遞歸向上搜索父接口就好了。
初始化階段是執行類構造器< clinit >()方法的過程。
< clinit >()方法是由編譯器自動收集類中的靜態變量的賦值動做和靜態語句塊中的語句合併產生的,編譯器收集的順序是由語句在源文件中出現的順序所決定的,靜態語句塊中只能訪問到定義在靜態語句塊以前的變量,定義在它以後的變量,在前面的靜態語句塊能夠賦值,可是不能訪問。
< clinit >()方法與類的構造函數不一樣,它不須要顯式地調用父類構造器,虛擬機會保證在子類的< clinit >()方法執行以前,父類的< clinit >()方法方法已經執行完畢。
簡單總結,感謝你寶貴的時間閱讀這篇文章!
本文參考了這篇博文
【深刻Java虛擬機】之四:類加載機制