JVM——類初始化之主動引用和被動引用

背景

  • 進(jìn)入正題前先說下背景,本人最近在了解一些JVM的知識,本文適合JVM初學(xué)者肆氓。本文完整代碼鏈接見文末

注:本著有目的學(xué)習(xí)的宗旨亚脆,本人在了解和涉獵JVM的過程中做院,對于一些JVM規(guī)范和指令大多是帶過,遠(yuǎn)遠(yuǎn)達(dá)不到對JVM熟悉的地步濒持。

  • OK键耕,啥也不說,上來先丟兩個名詞先柑营。在類的初始化過程中屈雄,對類的引用可分為兩大類:

    • 主動引用
    • 被動引用
  • 為了配合后續(xù)內(nèi)容,先上四個非常簡單的類官套,這幾個類看不懂就別往下看了酒奶,因為你的Java基礎(chǔ)還在來的路上:

public class SuperClass {
    static {
      System.out.println("SuperClass init!");
    }

    /**
     * 類靜態(tài)字段
     */
    public static int value = 123;
}

public class SubClass extends SuperClass {
    static {
      System.out.println("SubClass init!");
    }
}

public class ConstantsClass {
    static {
      System.out.println("ConstantsClass init!");
    }

    /**
     * 類常量
     */
    public static final int VALUE = 123;
}

public class MethodHandleClass {
    static {
        System.out.println("MethodHandleClass init");
    }

    public static void testMethodHandle(String str) {
        System.out.println(str);
    }
}

主動引用

  • 類的初始化階段,虛擬機規(guī)范嚴(yán)格規(guī)定了有且只有以下5種情況必須立即對類進(jìn)行“初始化”奶赔,我們把這5種場景中的行為惋嚎,稱為對一個類進(jìn)行主動引用

1. 遇到new站刑、getstatic另伍、putstatic和invokestaic這4條字節(jié)碼指令

  • 遇到new、getstatic绞旅、putstatic和invokestaic這4條字節(jié)碼指令時摆尝,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)類的初始化因悲。

    • new:創(chuàng)建類實例的指令(注意堕汞,與創(chuàng)建數(shù)組的指令不一樣——newarray、anewarray晃琳、multianewarray)
    • getstatic讯检、putstatic:訪問類字段的指令(static字段)
    • invokestatic:調(diào)用類方法的指令(static方法)
public class Initialization1 {
    public static void main(String[] args) {
        // 遇到new字節(jié)碼指令時,如果類沒有進(jìn)行過初始化卫旱,則需要先觸發(fā)類的初始化
        new SuperClass();
    }
}

// 輸出結(jié)果為
SuperClass init!

2. 反射調(diào)用

  • 對類進(jìn)行反射調(diào)用的時候视哑,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化
public class Initialization2 {
    public static void main(String[] args) {
        try {
            // 對類進(jìn)行反射調(diào)用的時候誊涯,如果類沒有進(jìn)行過初始化挡毅,則需要先觸發(fā)其初始化
            Class.forName("com.xpleemoon.classinit.SuperClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

// 輸出結(jié)果為
SuperClass init!

3. 父類未初始化

  • 當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化暴构,則需要觸發(fā)其父類的初始化
public class Initialization3 {
    public static void main(String[] args) {
        // 當(dāng)初始化一個類的時候跪呈,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化段磨,則需要觸發(fā)其父類的初始化
        new SubClass();
    }
}

// 輸出結(jié)果為
SuperClass init!
SubClass init!

4. 執(zhí)行的主類

  • 當(dāng)虛擬機啟動時,用戶需要指定一個要執(zhí)行的主類耗绿,虛擬機會先初始化這個主類(包含main()方法的一個執(zhí)行類)
public class Initialization4 {
    static {
        System.out.println("當(dāng)虛擬機啟動時苹支,用戶需要指定一個要執(zhí)行的主類,虛擬機會先初始化這個主類");
    }

    public static void main(String[] args) {

    }
}

// 輸出結(jié)果為
當(dāng)虛擬機啟動時误阻,用戶需要指定一個要執(zhí)行的主類债蜜,虛擬機會先初始化這個主類

5. 使用JDK 1.7 的動態(tài)語言支持

  • 當(dāng)使用JDK 1.7 的動態(tài)語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的結(jié)果是REF_getStatic究反、REF_putStatic寻定、REF_invokeStatic的方法句柄,并且這個方法句柄對應(yīng)的類沒有進(jìn)行過初始化精耐,則需要先觸發(fā)其初始化狼速。
public class Initialization5 {
    public static void main(String[] args) {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        try {
            MethodHandle testMethodHandle = lookup.findStatic(MethodHandleClass.class, "testMethodHandle", MethodType.methodType(void.class, String.class));
            try {
                testMethodHandle.invoke("當(dāng)使用JDK 1.7 的動態(tài)語言支持時,如果一個java.lang.invoke.MethodHandle實例最后的結(jié)果是\n" +
                        "REF_getStatic卦停、REF_putStatic向胡、REF_invokeStatic的方法句柄,并且這個方法句柄對應(yīng)的類沒有進(jìn)行過初始化惊完,則需要先觸發(fā)其初始化");
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

// 輸出結(jié)果為
MethodHandleClass init
當(dāng)使用JDK 1.7 的動態(tài)語言支持時僵芹,如果一個java.lang.invoke.MethodHandle實例最后的結(jié)果是
REF_getStatic、REF_putStatic小槐、REF_invokeStatic的方法句柄淮捆,并且這個方法句柄對應(yīng)的類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化

被動引用

  • 除主動引用(上述5中場景)之外本股,其他所有引用類的方式都不會觸發(fā)初始化,這些其他類的引用方式稱為被動引用桐腌。下面用三個例子來說明:

例子1——通過子類引用父類的靜態(tài)字段拄显,不會導(dǎo)致子類初始化

public class NotInitialization1 {
    public static void main(String[] args) {
      // 子類引用父類的靜態(tài)字段,不會導(dǎo)致子類初始化
      System.out.println(SubClass.value);
    }
}

// 輸出結(jié)果為
SuperClass init!
123

例子2——通過數(shù)組定義來引用類案站,不會觸發(fā)此類的初始化

public class NotInitialization2 {
    public static void main(String[] args) {
      // 注意躬审,這里的字節(jié)碼指令是newarray而非new,那么這段代碼的初始化就是類[SuperClass的初始化
      SuperClass[] sca = new SuperClass[10];
    }
}

// 輸出結(jié)果為無

例子3——常量在編譯階段會存入調(diào)用類的常量池中蟆盐,本質(zhì)上沒有直接引用到定義常量的類承边,因此不會觸發(fā)

定義常量的類的初始化

public class NotInitialization3 {
    public static void main(String[] args) {
      // 常量在編譯階段會存入調(diào)用類的常量池中,本質(zhì)上沒有直接引用到定義常量的類石挂,
      // 因此不會觸發(fā)定義常量的類的初始化
      System.out.println(ConstantsClass.VALUE);
    }
}

// 輸出結(jié)果為
123
  • 為了實驗本文內(nèi)容博助,請猛點代碼倉庫鏈接
  • 參考書目——周志明《深入理解Java虛擬機——JVM高級特性與最佳實踐》
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市痹愚,隨后出現(xiàn)的幾起案子富岳,更是在濱河造成了極大的恐慌蛔糯,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窖式,死亡現(xiàn)場離奇詭異蚁飒,居然都是意外死亡,警方通過查閱死者的電腦和手機萝喘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門淮逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人阁簸,你說我怎么就攤上這事爬早。” “怎么了强窖?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵凸椿,是天一觀的道長。 經(jīng)常有香客問我翅溺,道長脑漫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任咙崎,我火速辦了婚禮优幸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘褪猛。我一直安慰自己网杆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布伊滋。 她就那樣靜靜地躺著碳却,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笑旺。 梳的紋絲不亂的頭發(fā)上昼浦,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音筒主,去河邊找鬼关噪。 笑死,一個胖子當(dāng)著我的面吹牛乌妙,可吹牛的內(nèi)容都是我干的使兔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藤韵,長吁一口氣:“原來是場噩夢啊……” “哼虐沥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起泽艘,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤置蜀,失蹤者是張志新(化名)和其女友劉穎奈搜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盯荤,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡馋吗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了秋秤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片宏粤。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖灼卢,靈堂內(nèi)的尸體忽然破棺而出绍哎,到底是詐尸還是另有隱情,我是刑警寧澤鞋真,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布崇堰,位于F島的核電站,受9級特大地震影響涩咖,放射性物質(zhì)發(fā)生泄漏海诲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一檩互、第九天 我趴在偏房一處隱蔽的房頂上張望特幔。 院中可真熱鬧,春花似錦闸昨、人聲如沸蚯斯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拍嵌。三九已至,卻和暖如春循诉,著一層夾襖步出監(jiān)牢的瞬間横辆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工打洼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人逆粹。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓募疮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親僻弹。 傳聞我的和親對象是個殘疾皇子阿浓,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 本文根據(jù)《深入理解java虛擬機》第7章內(nèi)容整理 一、基本概念 虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存蹋绽,并...
    夢工廠閱讀 2,769評論 1 31
  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理芭毙,因此不免有一些不準(zhǔn)確的地方筋蓖,同時不同JDK版本的...
    高廣超閱讀 15,599評論 3 83
  • Java 虛擬機(Java virtual machine,JVM)是運行 Java 程序必不可少的機制退敦。JVM實...
    Rick617閱讀 864評論 0 0
  • 本系列主要記錄筆者在學(xué)習(xí) [深入理解Java虛擬機] 一書時的理解我們都知道在Java中粘咖,我們并不需要過多的在意內(nèi)...
    Robin_Lrange閱讀 789評論 0 0
  • 一、運行時數(shù)據(jù)區(qū)域 Java虛擬機管理的內(nèi)存包括幾個運行時數(shù)據(jù)內(nèi)存:方法區(qū)侈百、虛擬機棧瓮下、本地方法棧、堆钝域、程序計數(shù)器讽坏,...
    加油小杜閱讀 1,519評論 1 15