Java 類加載過程66IKIKIK8JU8UJ8OOUY;

Java 類加載過程

Class的生命週期

一個Class在虛擬機中的完整生命週期如下圖所示:

 

其中,驗證、整備、解析三個部分統稱爲連接。

需要說明的是,上述的流程只是描述了邏輯上各個階段的開始順序,實際過程中,各個階段可能是交錯進行,並不是一個階段等到另一個階段完全完成纔開始執行。

加載

加載一個Class需要完成以下3件事:

  • 通過Class的全限定名獲取Class的二進制字節流
  • 將Class的二進制內容加載到虛擬機的方法區
  • 在內存中生成一個java.lang.Class對象表示這個Class

獲取Class的二進制字節流這個步驟有多種方式:

  • 從zip中讀取,如:從jar、war、ear等格式的文件中讀取Class文件內容
  • 從網絡中獲取,如:Applet
  • 動態生成,如:動態代理、ASM框架等都是基於此方式
  • 由其他文件生成,典型的是從jsp文件生成相應的Class

校驗

驗證一個Class的二進制內容是否合法,主要包括4個階段:

  • 文件格式驗證,確保文件格式符合Class文件格式的規範。如:驗證魔數、版本號等。
  • 元數據驗證,確保Class的語義描述符合Java的Class規範。如:該Class是否有父類、是否錯誤繼承了final類、是否一個合法的抽象類等。
  • 字節碼驗證,通過分析數據流和控制流,確保程序語義符合邏輯。如:驗證類型轉換是合法的。
  • 符號引用驗證,發生於符號引用轉換爲直接引用的時候(轉換髮生在解析階段)。如:驗證引用的類、成員變量、方法的是否可以被訪問(IllegalAccessError),當前類是否存在相應的方法、成員等(NoSuchMethodError、NoSuchFieldError)。

準備

在準備階段,虛擬機會在方法區中爲Class分配內存,並設置static成員變量的初始值爲默認值。注意這裏僅僅會爲static變量分配內存(static變量在方法區中),並且初始化static變量的值爲其所屬類型的默認值。如:int類型初始化爲0,引用類型初始化爲null。即使聲明瞭這樣一個static變量:

public static int a = 123;

在準備階段後,a在內存中的值仍然是0, 賦值123這個操作會在中初始化階段執行,因此在初始化階段產生了對應的Class對象之後a的值纔是123 。

解析

解析階段,虛擬機會將常量池中的符號引用替換爲直接引用,解析主要針對的是類、接口、方法、成員變量等符號引用。在轉換成直接引用後,會觸發校驗階段的符號引用驗證,驗證轉換之後的直接引用是否能找到對應的類、方法、成員變量等。這裏也可見類加載的各個階段在實際過程中,可能是交錯執行。

初始化

初始化階段即開始在內存中構造一個Class對象來表示該類,即執行類構造器<clinit>()的過程。需要注意下,<clinit>()不等同於創建類實例的構造方法<init>()

  • <clinit>()方法中執行的是對static變量進行賦值的操作,以及static語句塊中的操作。
  • 虛擬機會確保先執行父類的<clinit>()方法。
  • 如果一個類中沒有static的語句塊,也沒有對static變量的賦值操作,那麼虛擬機不會爲這個類生成<clinit>()方法。
  • 虛擬機會保證<clinit>()方法的執行過程是線程安全的。
    因此,存在如下一種最簡單的單例模式的實現:

     public class Singleton {
         public static final INSTANCE = new Singleton();
         private Singleton() {
         }
     }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章