溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

由一道面試題理解類加載機(jī)制

發(fā)布時間:2020-07-10 17:59:59 來源:網(wǎng)絡(luò) 閱讀:131 作者:JAVA少女 欄目:編程語言

前言
不了解JVM的類加載機(jī)制你也可以coding,但是當(dāng)你了解之后,可以讓你在coding的時候避免很多坑,本文將以一道常見的面試題去剖析一下。本文參考 深入理解Java虛擬機(jī)(第2版) 。

 1 public class ClassLoadTest {
 2    private static ClassLoadTest test = new ClassLoadTest();
 3
 4    static int x;
 5    static int y = 0;
 6
 7    public ClassLoadTest() {
 8        x++;
 9        y++;
10    }
11
12    public static void main(String[] args) {
13        System.out.println(test.x);
14        System.out.println(test.y);
15    }
16}

這里大家可以先猜測一下答案,可能結(jié)果會出乎你的意料~

類加載過程

先用一個圖簡單的描述一下類加載的這個過程
由一道面試題理解類加載機(jī)制
加載
這個過程相當(dāng)于從本地或者網(wǎng)絡(luò)端去讀取一個字節(jié)流,然后將一些靜態(tài)儲存結(jié)構(gòu)轉(zhuǎn)換成方法區(qū)中運(yùn)行時期的數(shù)據(jù),最后生成一個代表這個類的Class對象,作為方法區(qū)訪問這個類的入口。

例如:

  • 咱們可以通過一個類的全限定名去加載類

  • 通過jar、war包去加載類

  • 通過http請求去第三方平臺上拉取指定的類來加載

  • 運(yùn)行時計算生成,例如Cglib動態(tài)代理等等

針對上述例子,這里是加載一個?ClassLoadTest.class?對象。

驗證

要理解這個環(huán)節(jié)并不是很難,一個東西要放到JVM上去運(yùn)行,咱們肯定得對其進(jìn)行一些過濾,不能啥都往上丟,這里的驗證簡單的舉幾個例子:

  • 文件格式的驗證:?
    ①是否以魔數(shù)0xCAFEBABE開頭;?
    ②主次版本號是否在當(dāng)前虛擬機(jī)處理范圍內(nèi);?
    ③常量池中的常量是否有不被支持的常量類型等等。

  • 元數(shù)據(jù)的驗證:?
    ①這個類是否有父類;?
    ②這個類的父類是否繼承了不被允許繼承的類(final修飾的類);?
    ③這個類不是抽象類,是否實(shí)現(xiàn)了所有接口中要實(shí)現(xiàn)的方法等等。

  • 字節(jié)碼的驗證:
    ①保證跳轉(zhuǎn)指令不會跳轉(zhuǎn)到方法體以外的字節(jié)碼指令上;?
    ②保證方法體中的類型轉(zhuǎn)換是有效的等等。

  • 符號引用的驗證:?
    ①能否通過類的全限定名去找到對應(yīng)的類;?
    ②符號引用中的類、字段、方法是否可以被當(dāng)前類訪問等等。

準(zhǔn)備過程

這個過程相當(dāng)于給類變量分配內(nèi)存并設(shè)置變量初始值的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。

針對上述例子:

1test = null;
2x = 0;
3y = 0;

注意:這里有個特殊情況,如果該字段被?final?修飾,那么在準(zhǔn)備階段改字段就會被設(shè)置成咱們自定義的值。?public static final int?value?= 11?,在準(zhǔn)備階段就會直接賦值11,并不是該變量的初始值。

解析過程

將符號引用轉(zhuǎn)換成直接引用的過程。這里有兩個名詞?符號引用?和?直接引用?。

  • 符號引用:符號引用與虛擬機(jī)的布局無關(guān),甚至引用的目標(biāo)不一定加載到了內(nèi)存中。符號可以是任何形式的字面量,只要使用時能夠準(zhǔn)確的定位到目標(biāo)即可。

  • 直接引用:直接引用可以直接指向目標(biāo)的指針、相對偏移量或是一個能間接定位到目標(biāo)的句柄。直接引用與虛擬機(jī)布局有關(guān),如果有了直接引用,那么引用的目標(biāo)必定已經(jīng)在內(nèi)存中存在。

而解析過程又會針對類、字段、方法進(jìn)行解析,解析失敗則會拋出相應(yīng)的異常。例如在解析時發(fā)現(xiàn)沒有訪問權(quán)限會拋出?java.lang.IllegalAccessException?異常,查詢不到引用字段會拋出?java.lang.NoSuchFieldException?異常,查詢不到方法會拋出?java.lang.NoSuchMethodException?異常等等。

初始化

在準(zhǔn)備階段,變量已經(jīng)賦值過系統(tǒng)要求的默認(rèn)值,在初始化階段,則會根據(jù)程序制定的主觀計劃去初始化類變量和其他資源。這句話聽起來有些繞口,根據(jù)上述例子,實(shí)際上就是:

1test = new ClassLoadTest();// x = 1;y =1
2y = 0;

這個過程,由于?x?咱們自己并沒有去設(shè)定一個值,所以初始化階段它不會發(fā)生任何改變,但是?y?咱們有設(shè)定一個值0,所以最后造成最終結(jié)果為?x = 1;y = 0?。

ps:在同一個類加載器下,一個類只會初始化一次。多個線程同時初始化一個類,只有一個線程能正常初始化,其他線程都會進(jìn)行阻塞等待,直到活動線程執(zhí)行初始化方法完畢。

總結(jié)

對于上面這個面試題,咱們用流程圖簡單的描述一下:

由一道面試題理解類加載機(jī)制

對Java技術(shù),架構(gòu)技術(shù)感興趣的同學(xué),歡迎加群,一起學(xué)習(xí),相互討論??梢垣@取免費(fèi)的學(xué)習(xí)資料,群號:614478470 點(diǎn)擊加入

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI