(轉) JVM執行流程
Java啟動流程:
1.java虛擬機器啟動的命令是通過java +xxx(類名,這個類中要有main方法)或者javaw啟動的。
2.執行命令後,系統第一步做的就是裝載配置,會在當前路徑中尋找jvm的config配置檔案。
3.找到jvm的config配置檔案之後會去定位jvm.dll這個檔案。這個檔案就是java虛擬機器的主要實現。
4.當找到匹配當前版本的jvm.dll檔案後,就會使用這個dll去初始化jvm虛擬機器。獲得相關的介面。之後找到main方法開始執行。
上面這個過程的描述雖然比較簡單,但是jvm的啟動流程基本都已經涵蓋在裡面了。
什麼是類載入
眾所周知,JVM載入的是.class檔案。其實,類的載入指的是將類的.class檔案中的二進位制資料讀入到記憶體中,將其放在執行時資料區的方法區內,然後在堆區建立一個java.lang.Class物件,用來封裝類在方法區內的資料結構。類的載入的最終產品是位於堆區中的Class物件,Class物件封裝了類在方法區內的資料結構,並且向Java程式設計師提供了訪問方法區內的資料結構的介面。
同時,JVM規範允許類載入器在預料某個類將要被使用時就預先載入它,如果在預先載入的過程中遇到了.class檔案缺失或存在錯誤,類載入器會在程式首次主動使用該類時會生成錯誤報告(LinkageError錯誤),如果這個類一直沒有被程式主動使用,那麼類載入器就不會報告錯誤。
類的載入過程
JVM將類的載入分為3個步驟: 1、裝載(Load)
2、連結(Link)
3、初始化(Initialize)
而連結(Link)又分3個步驟: 1,驗證
2,準備
3,解析
1,裝載
載入是類載入過程的第一個階段,在載入階段,虛擬機器需要完成以下三件事情: 1、通過一個類的全限定名來獲取其定義的二進位制位元組流。
2、將這個位元組流所代表的靜態儲存結構轉化為方法區的執行時資料結構。
3、在Java堆中生成一個代表這個類的java.lang.Class物件,作為對方法區中這些資料的訪問入口。
相對於類載入的其他階段而言,載入階段是獲取類的二進位制位元組流的最佳階段,因為開發人員既可以使用系統提供的類載入器來完成載入,也可以自定義自己的類載入器來完成載入。
載入階段完成後,虛擬機器外部的 二進位制位元組流就按照虛擬機器所需的格式儲存在方法區之中,而且在Java堆中也建立一個java.lang.Class類的物件,這樣便可以通過該物件訪問方法區中的這些資料。
2,連結
連結階段分為三個步驟:驗證、準備和解析。
- 驗證:確保被載入的類的正確性;
- 準備:為類的靜態變數分配記憶體,並將其初始化為預設值;
- 解析:把類中的符號引用轉換為直接引用。
驗證
驗證是連結第一步,這一階段的目的是為了確保Class檔案的位元組流中包含的資訊符合當前虛擬機器的要求,並且不會危害虛擬機器自身的安全。驗證階段大致會完成4個階段的檢驗動作:
- 檔案格式驗證:驗證位元組流是否符合Class檔案格式的規範;例如:是否以0xCAFEBABE開頭、主次版本號是否在當前虛擬機器的處理範圍之內、常量池中的常量是否有不被支援的型別。
- 元資料驗證:對位元組碼描述的資訊進行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的資訊符合Java語言規範的要求;例如:這個類是否有父類,除了java.lang.Object之外。
- 位元組碼驗證:通過資料流和控制流分析,確定程式語義是合法的、符合邏輯的。
- 符號引用驗證:確保解析動作能正確執行。
驗證階段是非常重要的,但不是必須的,它對程式執行期沒有影響,如果不需要驗證,那麼可以考慮採用-Xverifynone引數來關閉大部分的類驗證措施,以縮短虛擬機器類載入的時間。
準備
準備階段是正式為類變數分配記憶體並設定類變數初始值的階段,這些記憶體都將在方法區中分配。對於該階段有以下幾點需要注意:
1、這時候進行記憶體分配的僅包括類變數(static),而不包括例項變數,例項變數會在物件例項化時隨著物件一塊分配在Java堆中。
2、這裡所設定的初始值通常情況下是資料型別預設的零值(如0、0L、null、false等),而不是被在Java程式碼中被顯式地賦予的值。
解析
解析階段是虛擬機器將常量池內的符號引用替換為直接引用的過程,解析動作主要針對類或介面、欄位、類方法、介面方法、方法型別、方法控制代碼和呼叫限定符7類符號引用進行。符號引用就是一組符號來描述目標,可以是任何字面量。 直接引用就是直接指向目標的指標、相對偏移量或一個間接定位到目標的控制代碼。
3,初始化
初始化,為類的靜態變數賦予正確的初始值,JVM負責對類進行初始化,主要對類變數進行初始化。在Java中對類變數進行初始值設定有兩種方式:
1,宣告類變數是指定初始值。
2,使用靜態程式碼塊為類變數指定初始值。
類的初始化步驟或JVM初始化的步驟如下: 1)如果這個類還沒有被載入和連結,那先進行載入和連結 ;
2)假如這個類存在直接父類,並且這個類還沒有被初始化(注意:在一個類載入器中,類只能初始化一次),那就初始化直接的父類(不適用於介面);
3 ) 假如類中存在初始化語句(如static變數和static塊),那就依次執行這些初始化語句。