JVM是如何進行載入類的?

暮雪糊糊發表於2018-08-08

一個類只有被JVM虛擬機器載入過後才能使用,虛擬機器載入一個類需要進過:載入、連結、初始化這三步驟才能被使用。連結又分為:驗證、準備、解析三個步驟。下面我將圍繞這六個步驟進行講解。

       載入:虛擬機器將Class檔案中的位元組碼載入到記憶體中,那虛擬機器是如何將位元組碼載入到記憶體中的呢?答案是通過類載入器,說到類載入器,所有的類載入器都有一個共同的祖師爺(啟動類載入器),啟動類載入是由C++實現的,因此它沒有對應的Java物件,所以在Java中只能用Null來指代,除了啟動類載入器之外,其他的類載入器都是java.lang.ClassLoader的子類,因此有對應的Java物件,使用類載入器需要先載入它的父類類載入後才能使用,比如:只有啟動類載入器,載入到虛擬機器中後,其他的類載入器才能進行載入,當一個類需要載入時,類載入器會先讓父類載入器載入,如果父類載入器無法載入,子類載入器才會嘗試載入,這個規則叫雙親委派模型

       啟動類載入器:載入的都是最基礎、最重要的類,比如jre的lib目錄下的jar包中的類,除了啟動類載入器之外,還有擴充套件類載入器和應用類載入器,這三個類載入器是最重要的三個。

      擴充套件類載入器:它的父類載入器是啟動類載入器,它主要負責載入相對於通用、次要的類,比如:jre的lib\ext目錄下的jar包中的類。(注意,在Java9以後引入了模組系統,擴充套件類載入器改名為平臺類載入器,所以,除了少數幾個模組之外,其他模組都是由平臺類載入器進行載入的

     應用類載入器:它主要載入應用程式下的類,

    連結:連結可以分為:驗證、準備、解析三個階段,Java虛擬機器規範並沒有要求在連結過程中完成解析。它僅規定了:如果某些位元組碼使用了符號引用,那麼在執行這些位元組碼之前,需要完成對這些符號引用的解析。

        驗證階段主要是驗證位元組碼是否滿足Java虛擬機器的約束條件,

       準備階段主要是為類的靜態欄位分配記憶體,當然除了給靜態欄位分配記憶體之外,部分Java虛擬機器還會在此階段構造其他跟類層次相關的資料結構,比如實現虛方法的動態繫結的方法表,

         解析是將符號引用解析成實際引用,當解析的這個符號引用所對應的類如果沒有被載入,那麼這裡就會觸發這個類的載入,但是未必會觸發連結和初始化。(符號引用就是在Class檔案被載入到Java虛擬機器之前,這個類無法知道其他類以及其方法、欄位所對應的具體地址,甚至不知道自己的方法、欄位的地址,因此每當需要引用這些方法、欄位或者類時,Java編譯器會生成一個符號引用來代指。

  初始化:類載入的最後一步便是初始化,只要初始化以後這個類就可以被使用了,初始化主要是為標記為常量值的欄位賦值,以及執行<clinit>方法的過程,在Java程式碼中,如果要初始化一個靜態欄位,我們可以在宣告時直接賦值,也可以在靜態程式碼塊中對其賦值,如果賦值的的靜態欄位是被flnal所修飾過的,並且它的型別是基本型別或者是字串時,那麼該欄位便會被Java編譯器標記為常量值,其初始化直接由Java虛擬機器完成,除此之外的直接賦值操作,以及所有靜態程式碼塊中的程式碼,都會被Java編譯器置於一個叫<clinit>的方法中,為了保證該方法只被執行一次,Java虛擬機器會通過加鎖來確保該方法只被執行一個。

類的初始化會在以下8種情況下觸發:

       1.當虛擬機器啟動時,初始化使用者指定的主類;

       2.當遇到用以新建目標類例項的new指令是,初始化new指令的目標類;

       3.當遇到呼叫靜態方法的指令時,初始化靜態方法所在的類;

       4.當遇到訪問靜態欄位的指令時,初始化靜態欄位所在的類;

       5.子類初始化會觸發父類的初始化;

       6.如果一個介面定義了default方法,那麼直接實現或者間接實現該介面的類的初始化,會觸發該介面的初始化;、

       7.使用反射API對某個類進行反射呼叫時,初始化這個類;

       8.當初次呼叫MethodHandle例項時,初始化該MethodHandle指向的方法所在的類;

 

 

     

相關文章