java虛擬機原理:類加載過程詳解

一、Java 類加載過程

1、字節碼編譯

編寫好 Java 源碼 Student.java ,

使用 javac 將上述 Java 源碼編譯成 Class 字節碼文件 Student.class ,

2、加載

加載 : 通過 ” 類加載子系統 ” 將該字節碼文件 , 加載到 Java 虛擬機內存中 的 方法區 , 然後開始執行 ” 連接 ” 操作 ,

類加載時機 : Java 程序執行時 , 並不是一開始將所有的字節碼文件都加載到內存中 , 而是用到時才進行加載 ;

  • 通過 new 關鍵字創建實例對象 ;
  • 通過 Class 反射 獲取類 ;
  • 如 : Class.forName(“Xxx”) 獲取類 ;
  • 序列化 / 反序列化 ;
  • 調用 clone 克隆對象 ;
  • 有 main 函數的類 , 會默認自動加載 ;
  • 調用子類 , 如果之前沒有加載過父類 , 則 自動加載父類 ;

3、連接

連接操作 分為 3 3 3 個步驟 :

  • 驗證 : 對 字節碼文件 進行校驗 , 查看該字節碼格式是否正確 , 如 : 是否以 0xCAFEBABE 開頭 , 字段表 , 方發表 , 屬性表 等格式是否正確 , 進行校驗 ;
  • 校驗示例 : 假設校驗如下字節碼數據 , 原始數據是 【Java 虛擬機原理】Class 字節碼二進制文件分析 一 ( 字節碼文件附加信息 | 魔數 | 次版本號 | 主版本號 | 常量池個數 ) 二、字節碼文件示例 章節中的 Java 源碼 , Class 字節碼 , 字節碼附加信息 ;

在 Student 構造方法中 , 會調用到 1: invokespecial #1 父類構造方法 , 如果父類有有參的構造方法且沒有聲明無參構造方法 , 子類必須實現一個相同參數的構造方法 , 否則就會報錯 ;

Constant pool:
   #1 = Methodref          #4.#17         // java/lang/Object."<init>":()V
{
  public Student();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
}
  • 準備 : 在該階段 , 在 方法區 中 , 為 類中的靜態變量 進行內存劃分 , 並對這些靜態變量進行 默認值賦值 , 一般賦值 0 , null 等默認值 ; 即使靜態變量 static int a = 5 已經有瞭賦值 , 但是在該階段暫時給該靜態變量賦值 0 ;
  • 解析 : 將 ” 常量池 ” 中的 ” 符號引用 ” 轉為 ” 直接引用 ” ;

符號引用 : 下面就是 常量池中的 符號引用 , 引用是 以符號的形式表示出來 的 , 這並不是內存中的引用 ; 直接引用 是 將 #1 = Methodref #4.#17 樣式的 符號引用 轉為 指向內存地址 的 指針引用 ; JVM 線程棧 的 棧幀 中的 動態鏈接 , 就是持有的一個指向內存的指針 , 該指針指向 棧幀 對應方法 在運行時 常量池中的 內存地址 ; 該內存地址是在 方法區 中的 ;

Constant pool:
   #1 = Methodref          #4.#17         // java/lang/Object."<init>":()V
   #2 = Fieldref           #3.#18         // Student.name:Ljava/lang/String;
   #3 = Class              #19            // Student
   #4 = Class              #20            // java/lang/Object

( 分析的數據是 【Java 虛擬機原理】Class 字節碼二進制文件分析 一 ( 字節碼文件附加信息 | 魔數 | 次版本號 | 主版本號 | 常量池個數 ) 二、字節碼文件示例 章節中的 Java 源碼 , Class 字節碼 , 字節碼附加信息 ; )

4、初始化

初始化 : 對變量進行 指定賦值 ;

如 : 有靜態變量 static int a = 5 , 在 連接 過程中的 準備 階段 , 為該變量賦值默認值 0 ; 在 初始化 階段 , 為其賦值 代碼 中設置的真正的 指定初始值 5 ;

總結

借助下圖理解類加載過程 ;

在這裡插入圖片描述

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的等多內容!

推薦閱讀: