jvm類加載的過程

一個類從加載到虛擬機到使用結束從虛擬機卸載包括了加載、驗證、準備、解析、初始化、使用、卸載,即爲一個類的生命週期java

clipboard.png
下面來看一下類加載的過程,即加載、驗證、準備、解析、初始化5個階段都作了什麼事:安全

階段1:加載

加載階段虛擬機主要3件事:
  1. 經過類的全名獲取其二進制字節流;
  2. 將字節流表明的靜態結構轉化爲方法區識別的運行時數據結構;
  3. 在內存中實例化這個類的java.lang.Class對象(不必定在堆內存中的,HotSpot就將Class對象放在了方法區裏),程序訪問這個類在方法區中的類型數據時會經過這個類去訪問;
    以上三點虛擬機並不要求如何實現,只是一個規範,好比第一步,經過類全名獲取其二進制流,動態代理技術是在運行時獲取、JSP應用是根據jsp文件獲取並生成對應的Class以及從ZIP包中獲取(JAR、EAR、WA同理)等

階段2:驗證

驗證階段大致上會完成4個階段的驗證(文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證),以保證虛擬機中類的規範和安全。
  1. 文件格式驗證,校驗字節流是否複合Class文件的格式:數據結構

    • 驗證文件是否以魔數0xCAFEBABE(十六進制class文件中的前4個字節)開頭;
    • 主、次版本號(十六進制class文件中的第五、第6個字節)可否被當前版本的虛擬機處理;
    • 常量池中是否有不被支持的類型;
    • 指向常量的索引中是否指向了不存在的常量;
    • Class文件中各個部分以及文件自己是否有被刪除或附加的其餘信息;
    • ......
  2. 元數據類型,校驗語義是否符合Java語言規範的要求:多線程

    • 驗證類是否有父類(除了java.lang.Object);
    • 驗證父類是否繼承了不可被繼承的類;
    • 若是不是抽象類,那麼要判斷是否實現了父類或接口的所要求實現的全部方法;
    • ......
  3. 字節碼驗證,校驗類的方法體,肯定語義是否符合邏輯:jsp

    • 保證操做數棧中的數據類型與指令序列一致;
    • 保證跳轉指令不會跳到方法體外的字節碼指令上;
    • 保證方法體中的類型轉換有效;
    • ......

階段3:準備

準備階段是爲類變量分配內存並設置類變量初始值的階段

這裏所說的初始值並非指代碼賦的值,而是數據類型的默認值,如public static int value = 123; 在準備階段事後,value會被置爲0,而不是123。
同時要注意,public static final int value = 123; 這種使用final修飾的變量,在準備階段就會被賦值爲123,而不是初始值。spa


階段4:解析

解析階段會將常量池內的符號引用轉換爲直接引用,關於符號引用和直接引用的解釋以下:
  • 符號引用:以一組符號來描述所引用的目,好比定義了在類IntF中定義了intValue = 123,接着讓Test.foo中的a變量指向Intf.intValue:
public class Test{
        public void foo(){
            int a = Intf.intValue;
        }
    }
    class Intf{
        public static int intValue = 123;
    }

編譯代碼以後咱們用javap -verbose Test來查看class文件中的內容:線程

Constant pool:
   #1 = Methodref          #4.#12         // java/lang/Object."<init>":()V
   #2 = Fieldref           #13.#14        // Intf.intValue:I
   #3 = Class              #15            // Test
   #4 = Class              #16            // java/lang/Object
 // 省略部分代碼...
  public void foo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=1
         0: getstatic     #2                  // Field Intf.intValue:I
         3: istore_1
         4: return
      LineNumberTable:
        line 3: 0
        line 4:
能夠看到常量池第2項是一個符號引用,指向了Intf.intValue
  • 直接引用:就是咱們常說的指針或者句柄,直接引用的目標必定會在虛擬機內存中存在。

階段5:初始化

初始化階段是類加載的最後一個階段,主要執行類的<clinit>方法(不一樣與<init>方法,<init>方法是在顯式調用constructor時執行,而<clinit>方法在初始化階段就會執行),<clinit>()方法會執行賦值操做和執行靜態語句快中的內容,換句話說,若是代碼中沒有靜態語句塊和賦值操做,那麼就能夠沒有<clinit>()方法。
這個階段虛擬機會保證父類的<clinit>()方法會在子類的<clinit>()方法前執行,並且在多線程環境中,虛擬機會保證<clinit>()方法的同步。代理

參考文獻:《深刻理解Java虛擬機》 - 周志明指針

相關文章
相關標籤/搜索