1. 程式人生 > >JVM運行和類加載全過程

JVM運行和類加載全過程

過程 符號 構造器 機制 安全 jpg end per 運行時

為什麽研究類加載全過程?

  • 有助於連接JVM運行過程
  • 更深入了解java動態性(解熱部署,動態加載),提高程序的靈活性

類加載機制

  • JVMclass文件加載到內存,並對數據進行校驗、解析和初始化,最終形成JVM可以直接使用的java類型的全過程。

技術分享圖片

  • 加載
    • class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個代表這個類的java.lang.Class對象,作為方法區類數據的訪問入口,這個過程需要類加載器參與。

技術分享圖片

  • 鏈接
    java類的二進制代碼合並到JVM的運行狀態之中的過程
    •   驗證:確保加載的類信息符合JVM規範,沒有安全方面的問題
    •   準備:正式為類變量(static變量)分配內存並設置類變量初始值的階段,這些內存都將在方法去中進行分配
    •   解析:虛擬機常量池的符號引用替換為字節引用過程
  • 初始化
    • 初始化階段是執行類構造器<clinit>()方法的過程。類構造器<clinit>()方法是由編譯器自動收藏類中的所有類變量的賦值動作和靜態語句塊(static)中的語句合並產生
    • 當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化
    • 虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確加鎖和同步
    • 當範圍一個Java類的靜態域時,只有真正聲名這個域的類才會被初始化

1

public class Demo01 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.width);
    }
}

class A{
    public static int width=100; //靜態變量,靜態域 field
    static{
        System.out.println("靜態初始化類A");
        width = 300 ;
    }
    public A() {
        System.out.println("創建A類的對象");
    }
}

分析:

技術分享圖片

說明:

內存中存在棧、堆(放創建好的對象)、方法區(實際也是一種特殊堆)

1JVM加載Demo01時候,首先在方法區中形成Demo01類對應靜態數據(類變量、類方法、代碼),同時在堆裏面也會形成java.lang.Class對象(反射對象),代表Demo01類,通過對象可以訪問到類二進制結構。然後加載變量A類信息,同時也會在堆裏面形成a對象,代表A類。

2main方法執行時會在棧裏面形成main方法棧幀,一個方法對應一個棧幀。如果main方法調用了別的方法,會在棧裏面挨個往裏壓,main方法裏面有個局部變量A類型的a,一開始a值為null,通過new調用類A的構造器,棧裏面生成A()方法同時堆裏面生成A對象,然後把A對象地址付給棧中的a,此時a擁有A對象地址。

3、當調用A.width時,調用方法區數據。

當類被引用的加載,類只會加載一次

  • 類的主動引用(一定會發生類的初始化)
    • new一個類的對象
    • 調用類的靜態成員(除了final常量)和靜態方法
    • 使用java.lang.reflect包的方法對類進行反射調用
    • 當虛擬機啟動,java Demo01,則一定會初始化Demo01類,說白了就是先啟動main方法所在的類
    • 當初始化一個類,如果其父類沒有被初始化,則先初始化它父類
  • 類的被動引用(不會發生類的初始化)
    • 當訪問一個靜態域時,只有真正聲名這個域的類才會被初始化
      • 通過子類引用父類的靜態變量,不會導致子類初始化
    • 通過數組定義類的引用,不會觸發此類初始化
    • 引用常量不會觸發此類的初始化(常量在編譯階段就存入調用類的常量池中了)

例2:

public class Demo01 {
    static{
        System.out.println("靜態初始化Demo01");
    }
    
    
    public static void main(String[] args) throws Exception {
        System.out.println("Demo01的main方法!");
        System.out.println(System.getProperty("java.class.path"));
        
        //主動引用
//        new A();
//        System.out.println(A.width);
//        Class.forName("com.sinosoft.test.A");
        
        
        //被動引用
//        System.out.println(A.MAX);
//        A[] as = new A[10];
        System.out.println(B.width);//B類不會被加載
        
    }
}

class B  extends A {
    static {
        System.out.println("靜態初始化B");
    }
}

class A extends A_Father {
    public static int width=100;   //靜態變量,靜態域    field
    public static final  int MAX=100; 
    
    static {
        System.out.println("靜態初始化類A");
        width=300;
    }
    public A(){
        System.out.println("創建A類的對象");
    }
}

class A_Father extends Object {
    static {
        System.out.println("靜態初始化A_Father");
    }
}

JVM運行和類加載全過程