文章目錄
- 一、Java 類加載程序
- 0、位元組碼編譯
- 1、加載
- 2、連接
- 3、初始化
- 總結
一、Java 類加載程序
0、位元組碼編譯
撰寫好 Java 原始碼 Student.java ,
使用 javac 將上述 Java 原始碼編譯成 Class 位元組碼檔案 Student.class ,
1、加載
加載 : 通過 " 類加載子系統 " 將該位元組碼檔案 , 加載到 Java 虛擬機記憶體中 的 方法區 , 然后開始執行 " 連接 " 操作 ,
類加載時機 : Java 程式執行時 , 并不是一開始將所有的位元組碼檔案都加載到記憶體中 , 而是用到時才進行加載 ;
- 通過 new 關鍵字創建實體物件 ;
- 通過 Class 反射 獲取類 ; 如 : Class.forName(“Xxx”) 獲取類 ;
- 序列化 / 反序列化 ;
- 呼叫 clone 克隆物件 ;
- 有 main 函式的類 , 會默認自動加載 ;
- 呼叫子類 , 如果之前沒有加載過父類 , 則 自動加載父類 ;
2、連接
連接操作 分為 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 位元組碼 , 位元組碼附加資訊 ; )
3、初始化
初始化 : 對變數進行 指定賦值 ;
如 : 有靜態變數 static int a = 5 , 在 連接 程序中的 準備 階段 , 為該變數賦值默認值 0 ; 在 初始化 階段 , 為其賦值 代碼 中設定的真正的 指定初始值 5 ;
總結
借助下圖理解類加載程序 ;

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/298468.html
標籤:其他
