自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JVM 為什么需要類加載機(jī)制?深入淺出 JVM 類加載原理

開發(fā) 前端
在 Java 中,類加載機(jī)制是 Java 虛擬機(jī)(JVM)將 .class 文件加載到內(nèi)存并轉(zhuǎn)化為可以運(yùn)行的 Class 對象的過程。簡單來說,類加載機(jī)制是讓“代碼變?yōu)楝F(xiàn)實(shí)”的第一步!

類加載機(jī)制是什么?

在 Java 中,類加載機(jī)制是 Java 虛擬機(jī)(JVM)將 .class 文件加載到內(nèi)存并轉(zhuǎn)化為可以運(yùn)行的 Class 對象的過程。簡單來說,類加載機(jī)制是讓“代碼變?yōu)楝F(xiàn)實(shí)”的第一步!

你可能會(huì)問,為什么需要類加載機(jī)制? 因?yàn)?Java 是一門 動(dòng)態(tài)語言,類可以在運(yùn)行時(shí)加載、鏈接和初始化,這種靈活性讓 Java 能夠?qū)崿F(xiàn)跨平臺運(yùn)行、高效的內(nèi)存管理和模塊化架構(gòu)。

類加載的三個(gè)階段

根據(jù)《Java 虛擬機(jī)規(guī)范》,類的生命周期包括以下三個(gè)主要階段:加載、鏈接初始化

而其中鏈接又分為三個(gè)子階段:驗(yàn)證(Verification)、準(zhǔn)備(Preparation)、解析(Resolution)。

圖片圖片

我們逐一拆解這些階段的工作原理和流程。

加載(Loading)


Chaya:類加載階段作用是什么?非要加載嗎?

主要是使用 "類加載器" 將本地或者遠(yuǎn)程網(wǎng)絡(luò)中的字節(jié)碼文件,通過讀字節(jié)流的方式加載到 Java 虛擬機(jī)內(nèi)存中。在加載階段中 Java 虛擬機(jī)主要完成以下三件事情:

  • 通過一個(gè)類的全限定名稱來獲取定義此類的二進(jìn)制字節(jié)流。
  • 將這個(gè)字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
  • 在內(nèi)存中生成一個(gè)代表這個(gè)類的 java.lang.Class 對象,作為方法區(qū)中這個(gè)類的各種數(shù)據(jù)的訪問入口。

加載是類加載的第一步,JVM 需要完成以下任務(wù):

圖片圖片

  1. 讀取 Class 文件:通過類的全限定名找到對應(yīng)的 .class 文件。
  2. 轉(zhuǎn)換為 JVM 可識別的結(jié)構(gòu):將 Class 文件的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為 JVM 的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。
  3. 創(chuàng)建 Class 對象:在內(nèi)存中創(chuàng)建 java.lang.Class 對象,作為該類的入口。

示例。

Class<?> clazz = Class.forName("com.example.MyClass");

這段代碼會(huì)觸發(fā) MyClass 的加載,將其 .class 文件讀取到內(nèi)存中,并生成 Class 對象。

鏈接(Linking)

鏈接 是將 Class 文件中的符號引用解析為直接引用的過程,分為以下三個(gè)子階段:

  1. 驗(yàn)證(Verification)確保 Class 文件的字節(jié)碼格式和內(nèi)容符合 JVM 的規(guī)范。

驗(yàn)證文件格式:Class 文件是否以 0xCAFEBABE 開頭。

驗(yàn)證字節(jié)碼:指令是否符合 JVM 規(guī)范,數(shù)據(jù)類型是否匹配。

  1. 準(zhǔn)備(Preparation)為類的靜態(tài)變量分配內(nèi)存,并設(shè)置默認(rèn)值。

例如:static int a = 10; 在準(zhǔn)備階段,a 的初始值是 0。

  1. 解析(Resolution)將符號引用替換為內(nèi)存地址的直接引用。

符號引用java.lang.String

直接引用:指向 String 類在內(nèi)存中的地址。

驗(yàn)證階段 (Verification)

驗(yàn)證階段的主要目的是對字節(jié)碼字節(jié)流進(jìn)行校驗(yàn),判斷其內(nèi)容是否符合當(dāng)前虛擬機(jī)的規(guī)范,以確保被加載的代碼運(yùn)行后不會(huì)對虛擬機(jī)造成損害。

大多數(shù)虛擬機(jī)大致都會(huì)對 文件格式、元數(shù)據(jù)、字節(jié)碼、符號引用 幾項(xiàng)內(nèi)容進(jìn)行校驗(yàn)。

文件格式驗(yàn)證

文件格式驗(yàn)證主要是對 字節(jié)流格式 進(jìn)行校驗(yàn),判斷其是否符合字節(jié)碼文件格式規(guī)范,并且還要判斷其是否可以運(yùn)行在當(dāng)前版本的虛擬機(jī)中。比如:

序號

描述

1

驗(yàn)證是否以 0XCAFEBABE 開頭

2

驗(yàn)證主、次版本號,是否包含在當(dāng)前虛擬機(jī)支持的版本范圍內(nèi)

3

驗(yàn)證字節(jié)碼常量池中的常量類型,是否都被虛擬機(jī)所支持

4

驗(yàn)證指向常量的各種索引值,是否有指向不存在的常量或不符合類型的常量

5

驗(yàn)證 CONSTANT_Utf8_info 類型常量中,是否有不符合 UTF-8 編碼的數(shù)據(jù)

6

驗(yàn)證字節(jié)碼文件中各個(gè)部分及文件本身,是否有被刪除或附加的其他信息

文件格式驗(yàn)證的主要目的其實(shí)就是為了保證加載的字節(jié)碼可以被正確地解析并存儲在方法區(qū)內(nèi)。

元數(shù)據(jù)驗(yàn)證

元數(shù)據(jù)驗(yàn)證主要是對 字節(jié)碼 中的 元數(shù)據(jù)信息 進(jìn)行語法校驗(yàn),避免存在不符合 Java 語法規(guī)范的元數(shù)據(jù)信息。比如:

序號

描述

1

驗(yàn)證當(dāng)前類的父類是否繼承了不允許被繼承的類,比如被 final 修飾的類

2

驗(yàn)證當(dāng)前類是否有父類,一般情況下除了 java.lang.Object 外,所有的類都應(yīng)當(dāng)有父類

3

驗(yàn)證如果當(dāng)前類不是抽象類,則當(dāng)前類是否實(shí)現(xiàn)了其父類或接口之中要求實(shí)現(xiàn)的所有方法

4

驗(yàn)證當(dāng)前類中的字段或方法是否與父類有沖突,比如當(dāng)前類覆蓋了父類的 final 字段,或者當(dāng)前類實(shí)現(xiàn)的方法參數(shù)都一致,但返回值的類型卻不同,導(dǎo)致不符合方法重載規(guī)則等情況

字節(jié)碼驗(yàn)證

字節(jié)碼驗(yàn)證主要是對 數(shù)據(jù)流控制流 進(jìn)行分析,以確保其語法合規(guī)且符合邏輯。

符號引用驗(yàn)證

符號引用驗(yàn)證主要對 字節(jié)碼常量池常量 的各種 符號引用 進(jìn)行校驗(yàn),確保當(dāng)前類引用到的其它類或者方法是真實(shí)存在且有權(quán)限訪問的。如果符號引用中關(guān)聯(lián)的類無法在系統(tǒng)中查找到,就會(huì)拋出 NoClassDefFoundError 錯(cuò)誤,如果符號引用中關(guān)聯(lián)的方法無法找到,則會(huì)拋出 NoSuchMethodError 錯(cuò)誤。

準(zhǔn)備階段 (Preparation)

準(zhǔn)備階段主要是用于對類或接口中的 "靜態(tài)變量" 分配內(nèi)存空間,以及對變量設(shè)置默認(rèn)的初始值。

準(zhǔn)備階段和初始化階段,這兩個(gè)階段都是用于對靜態(tài)變量設(shè)置值,概念上容易混淆,所以這里需要特別說明一下,準(zhǔn)備階段只是對靜態(tài)變量設(shè)置初始默認(rèn)值,而真正賦值操作是在初始化階段完成的。

例如,下面示例代碼在執(zhí)行時(shí):

public class A {
    static int test = 999;
}
  • 準(zhǔn)備階段會(huì)對變量 test 設(shè)置默認(rèn)值 0;
  • 初始化階段會(huì)對變量 test 賦予初始值 999

解析階段 (Resolution)

解析階段主要是用于將 字節(jié)碼常量池 中的 符號引用 替換為 直接引用 的過程。

  • 符號引用 (Symbolic References): 符號引用就是用于描述引用目標(biāo)的一組符號,它可以是任何形式的字面量 (只要符合 Java 虛擬機(jī)規(guī)范)。
  • 直接引用 (Direct References): 直接引用可以是直接指向目標(biāo)的指針、相對偏移量,或者是一個(gè)能間接定位到目標(biāo)的句柄。

初始化(Initialization)

  1. 初始化階段是類加載的最后一步,也是最重要的階段。此階段會(huì)執(zhí)行靜態(tài)變量的賦值操作和靜態(tài)代碼塊。
    初始化的觸發(fā)條件
    類的初始化順序

先初始化父類

再初始化當(dāng)前類的靜態(tài)變量和靜態(tài)代碼塊。

使用 new 關(guān)鍵字實(shí)例化對象時(shí)。

訪問類的靜態(tài)字段或靜態(tài)方法時(shí)。

使用反射調(diào)用類時(shí)


唐二婷:初始化階段有啥用?可以談戀愛嗎?

初始化階段主要是執(zhí)行 類構(gòu)造器 方法 <clinit>(),該方法不需要定義,代碼在經(jīng)過 Javac 編譯器編譯時(shí),會(huì)自動(dòng)收集類中的所有 類變量 的賦值動(dòng)作和 靜態(tài)代碼塊 中的語句,對這些代碼進(jìn)行合并,形成類構(gòu)造器 <clinit>() 。

在執(zhí)行類構(gòu)造器 <clinit>() 時(shí),會(huì)對類中的 類變量靜態(tài)代碼塊 進(jìn)行初始化賦值操作,如果該類存在父類,則會(huì)先執(zhí)行父類中的類構(gòu)造器 <clinit>(),對父類中的 類變量靜態(tài)代碼塊 進(jìn)行初始化。

示例如下。

public class FatherCLass {

    public static int number;

    static {
        System.out.println(number);
        System.out.println("父類 static{} 初始化");
    }

}

子類:

public class SubInitialization extends FatherCLass {

    static{
        // number 屬于父類的屬性,這里要能執(zhí)行成功,說明父類已經(jīng)加載
        number = 100;
        System.out.println("子類 static{} 初始化");
    }

    public static void main(String[] args) {
        System.out.println(number);
    }

}

執(zhí)行時(shí)輸出如下:

0
父類 static{} 初始化
子類 static{} 初始化
100


責(zé)任編輯:武曉燕 來源: 碼哥跳動(dòng)
相關(guān)推薦

2024-12-12 09:00:28

2023-10-31 16:00:51

類加載機(jī)制Java

2019-10-10 16:25:02

JVM數(shù)據(jù)多線程

2021-10-05 20:29:55

JVM垃圾回收器

2021-09-24 08:10:40

Java 語言 Java 基礎(chǔ)

2023-08-02 08:38:27

JVM加載機(jī)制

2023-05-05 18:33:15

2017-09-20 08:07:32

java加載機(jī)制

2021-04-29 11:18:14

JVM加載機(jī)制

2017-03-08 10:30:43

JVMJava加載機(jī)制

2024-03-12 07:44:53

JVM雙親委托機(jī)制類加載器

2012-05-21 09:58:30

動(dòng)態(tài)創(chuàng)建類Cocoa

2012-05-21 09:51:25

對象Cocoa

2022-10-08 08:34:34

JVM加載機(jī)制代碼

2021-07-20 15:20:02

FlatBuffers阿里云Java

2020-10-26 11:20:04

jvm類加載Java

2024-03-08 08:26:25

類的加載Class文件Java

2019-12-30 11:25:06

Jvm運(yùn)行java

2020-05-20 22:13:26

JVM加載機(jī)制虛擬機(jī)

2018-12-25 08:00:00

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號