最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java JVM類加載機(jī)制解讀

 更新時間:2021年11月17日 10:30:51   作者:小玄ks  
JVM將class文件字節(jié)碼文件加載到內(nèi)存中, 并將這些靜態(tài)數(shù)據(jù)轉(zhuǎn)換成方法區(qū)中的運(yùn)行時數(shù)據(jù)結(jié)構(gòu),在堆(并不一定在堆中,HotSpot在方法區(qū)中)中生成一個代表這個類的java.lang.Class 對象,作為方法區(qū)類數(shù)據(jù)的訪問入口,接下來將詳細(xì)講解JVM類加載機(jī)制

1.什么是類加載

首先你要知道一個類的從被加載到虛擬機(jī)內(nèi)存中開始,到被初始化為止,是為類加載的整個過程。下圖就是類加載的整個過程:

在這里插入圖片描述

一個類只有經(jīng)歷了加載、驗(yàn)證、準(zhǔn)備、解析、初始化這五個關(guān)卡才能被認(rèn)為是實(shí)現(xiàn)了類加載。這,就是類加載。

注意一點(diǎn):上面五個過程并不是按部就班地“完成”,而是按部就班地“執(zhí)行”(除解析過程外)。執(zhí)行時一定是先開始加載,再開始驗(yàn)證,但加載過程中也可能會直接開始驗(yàn)證。

2.類加載的過程

2.1加載

“加載”只是是“類加載”過程的第一個階段,關(guān)于在什么時候開始,規(guī)范并沒有進(jìn)行強(qiáng)制約束,可以讓虛擬機(jī)自行把握。在這個階段中,Java虛擬機(jī)需要完成以下三件事:

1)通過一個類的全限定名來獲取這個類的二進(jìn)制字節(jié)流

2)將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時數(shù)據(jù)結(jié)構(gòu)

3)在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的方問入口

可以用一句話概括:加載是一個讀取Class文件,將其轉(zhuǎn)化為某種靜態(tài)數(shù)據(jù)結(jié)構(gòu)存儲在方法區(qū)內(nèi),并在堆中生成一個便于用戶調(diào)用的java.lang.Class類型的對象的過程

2.2驗(yàn)證

驗(yàn)證是連接階段的第一步,這個階段的目的是確保Class文件的字節(jié)流中包含的信息符合約束要求,,保證這些信息被當(dāng)做代碼運(yùn)行后不會危害虛擬機(jī)自身的安全。

這一過程了解即可。

2.3準(zhǔn)備

準(zhǔn)備階段是正式為類中定義的變量(這里說的是靜態(tài)變量,也就是被static修飾的變量)分配內(nèi)存,并設(shè)置類變量初始值的階段。

這里有兩點(diǎn)需要強(qiáng)調(diào):

1)首先這里進(jìn)行內(nèi)存分配的僅僅是類變量,而不包括實(shí)例變量,實(shí)例變量將會在對象實(shí)例化時隨著對象一起分配在Java堆中。

2)其次這里設(shè)置的初始值“通常情況”下是數(shù)據(jù)的零值,而不是用戶本身對它賦的初值。

如下代碼:

public static int a = 10;

變量a在準(zhǔn)備階段后的初始值是0,而不是10,因?yàn)楝F(xiàn)在只是在類加載過程中,還沒有執(zhí)行任何方法。

上面說到“通常情況”,那就說明還有特殊情況咯,加修飾詞final時:

public static final int a = 10;

這時在準(zhǔn)備階段虛擬機(jī)就會將a設(shè)置為10。其實(shí)也不難理解:我們將它設(shè)置為常量,那就肯定在任何時候都不能修改啊,天子犯法與庶民同罪!

2.4解析

解析階段是Java虛擬機(jī)將常量池內(nèi)的符號引用替換為直接引用的過程,這一過程也可能在初始化后進(jìn)行,并不一定和流程圖的執(zhí)行順序一樣。

符號引用:符號引用以一組符號來描述所引用的目標(biāo),符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標(biāo)即可。

直接引用:直接引用是可以直接指向目標(biāo)的指針、相對偏移量或者是一個能間接定位到目標(biāo)的句柄。

這一過程比較復(fù)雜,有興趣可以參考《深入理解Java虛擬機(jī)》

2.5初始化【重中之重之重中重】

類的初始化階段是類加載過程的最后一個階段。在這個階段Java虛擬機(jī)才開始真正執(zhí)行類中編寫的Java程序代碼。

初始化階段有以下六種情況必須立即對類進(jìn)行“初始化”:

  • 1)使用new關(guān)鍵字實(shí)例化對象的時候
  • 2)讀取或設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時候
  • 3)調(diào)用一個類的靜態(tài)方法的時候
  • 4)使用java.lang.reflect包的方法對類進(jìn)行反射調(diào)用的時候,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。
  • 5)當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化,則需要先觸發(fā)其父類的初始化。
  • 6)當(dāng)虛擬機(jī)啟動時,用戶需要指定一個要執(zhí)行的主類(包含main()方法的那個類),虛擬機(jī)會先初始化這個主類。

光說不行,主要看

第一段代碼:

package com.bit.JVMTest;

class Father {

    public  static int a = 10;
    
    static {
        System.out.println("爸爸靜態(tài)代碼塊");
    }
}


class Son extends Father{

    public static int b = 20;
    
    static {
        System.out.println("兒子靜態(tài)代碼塊");
    }
}


public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(Son.b);
    }
}

運(yùn)行結(jié)果:
爸爸靜態(tài)代碼塊
兒子靜態(tài)代碼塊
20

首先Son.b是在讀取Son類自己的靜態(tài)字段,這點(diǎn)符合上面六中情況的第二種:讀取或設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時候需要進(jìn)行初始化。

其次Son類繼承Father類,也就符合第五條:當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化,則需要先觸發(fā)其父類的初始化,所以我們先初始化的應(yīng)該是Father類,然后是Son類。

因此,打印的內(nèi)容首先是爸爸靜態(tài)代碼塊(父類先初始化),然后是兒子靜態(tài)代碼塊(子類再初始化),最后是我們想要打印的b(20)本身。

再看

第二段代碼:

package com.bit.JVMTest;

class  grandFather{
    static{
        System.out.println("爺爺靜態(tài)代碼塊");
    }
}

class Father extends grandFather{

    public  static int a = 10;
    
    static {
        System.out.println("爸爸靜態(tài)代碼塊");
    }
}


class Son extends Father{

    public static int b = 20;
    
    static {
        System.out.println("兒子靜態(tài)代碼塊");
    }
}


public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(Son.a);
    }
}

運(yùn)行結(jié)果:
爺爺靜態(tài)代碼塊
爸爸靜態(tài)代碼塊
10

首先要明確:Son.a是在讀取父類Father類的靜態(tài)字段(注意a字段在Son類的父類中),而不是讀取Son類本身的靜態(tài)字段

因此這次不會初始化Son類本身。

因此這次不會初始化Son類本身。

因此這次不會初始化Son類本身。

其它的和第一段代碼很相似:JVM在初始化Father類的時候,發(fā)現(xiàn)這個類還有一個父類沒有被初始化,那就先初始化它的父類:grandFather。

因此,打印的內(nèi)容首先是爺爺靜態(tài)代碼塊(Father類的父類先初始化),然后是爸爸靜態(tài)代碼塊(Father類再初始化),最后是我們想要打印的a(10)本身。

第三段代碼:

package com.bit.JVMTest;

class  grandFather{
    static{
        System.out.println("爺爺靜態(tài)代碼塊");
    }
}

class Father extends grandFather{

    public final static int a = 10;
    
    static {
        System.out.println("爸爸靜態(tài)代碼塊");
    }
}


class Son extends Father{

    public static int b = 20;
    
    static {
        System.out.println("兒子靜態(tài)代碼塊");
    }
}


public class ClassLoaderTest {
    public static void main(String[] args) {
        System.out.println(Son.a);
    }
}

運(yùn)行結(jié)果:10

看到這里是不是想說臥**你*個*。

別急別急,這里的主函數(shù)調(diào)用雖然和第二段代碼一樣,但是注意?。。∥覀兘oa這個靜態(tài)字段加了一個final修飾符。

再看六條中的第(2)條:讀取或設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時候會觸發(fā)類加載。

也就是說我們讀取的a是被final修飾的,讀取這種靜態(tài)字段并不會引起任何類的初始化,所以就直接打印a(10)了。

再看

最后一段代碼:

package com.bit.JVMTest;


class Father {

   public Father(){
       System.out.println("爸爸構(gòu)造方法");
   }

    static {
        System.out.println("爸爸靜態(tài)代碼塊");
    }

    {
        System.out.println("爸爸普通代碼塊");
    }
}

class Son extends Father{
    public Son(){
        System.out.println("兒子構(gòu)造方法");
    }

    static {
        System.out.println("兒子靜態(tài)代碼塊");
    }

    {
        System.out.println("兒子普通代碼塊");
    }
}

public class ClassLoaderTest extends Son{
    public static void main(String[] args) {
        System.out.println("開始");
        new Son();//這里實(shí)例化一個Son類的對象
        System.out.println("結(jié)束");
    }
}

運(yùn)行結(jié)果:
 爸爸靜態(tài)代碼塊
 兒子靜態(tài)代碼塊
 開始
 爸爸普通代碼塊
 爸爸構(gòu)造方法
 兒子普通代碼塊
 兒子構(gòu)造方法
 結(jié)束

看到這里是不是欲哭無淚,我**不學(xué)了我。別急先聽我細(xì)細(xì)分析一波~
這里有一個細(xì)節(jié):主類繼承了Son類!,這貌似沒什么啊,但是還有一個細(xì)節(jié):我們的main()方法是主類中的靜態(tài)方法!看到這里是不是明白了些什么?

沒錯!當(dāng)我們調(diào)用main()方法的時候,就引起了主類的初始化,主類繼承Son類,Son類繼承Father類,所以就先進(jìn)行Father類的初始化:打印爸爸靜態(tài)代碼塊,接著Son類初始化:打印兒子靜態(tài)代碼塊,最后該終于我主類初始化了:代碼中沒什么可以初始化的…(尷尬)。

接下來是第二階段:執(zhí)行main()方法:

1.先打?。洪_始字樣。

2.接著是構(gòu)造 Son()實(shí)例,那么就會先構(gòu)造它的父類Father()的實(shí)例:構(gòu)造實(shí)例時按照先執(zhí)行代碼塊,再執(zhí)行構(gòu)造方法的順序來。所以就先打印了:爸爸普通代碼塊、爸爸構(gòu)造方法 這幾個大字。然后再執(zhí)行構(gòu)造Son()的實(shí)例,構(gòu)造順序一樣,所以就后打印了:兒子普通代碼塊、兒子構(gòu)造方法 這幾個大字。

3.最后打?。航Y(jié)束字樣。

此時main()才方法真正結(jié)束。

總結(jié)

我們平常所說的類加載體現(xiàn)在代碼上就是初始化這一階段,我這里結(jié)束的也僅限于此,想了解詳細(xì)的類加載可以參考《深入理解Java虛擬機(jī)》這本書,也可以看其他博主的知識總結(jié)。感謝你能看到這里!

到此這篇關(guān)于Java JVM類加載機(jī)制解讀的文章就介紹到這了,更多相關(guān)Java JVM 類加載機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java NIO無法綁定指定IP和端口解決方案

    Java NIO無法綁定指定IP和端口解決方案

    這篇文章主要介紹了Java NIO無法綁定指定IP和端口解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • Spring事務(wù)傳播中嵌套調(diào)用實(shí)現(xiàn)方法詳細(xì)介紹

    Spring事務(wù)傳播中嵌套調(diào)用實(shí)現(xiàn)方法詳細(xì)介紹

    Spring事務(wù)的本質(zhì)就是對數(shù)據(jù)庫事務(wù)的支持,沒有數(shù)據(jù)庫事務(wù),Spring是無法提供事務(wù)功能的。Spring只提供統(tǒng)一的事務(wù)管理接口,具體實(shí)現(xiàn)都是由數(shù)據(jù)庫自己實(shí)現(xiàn)的,Spring會在事務(wù)開始時,根據(jù)當(dāng)前設(shè)置的隔離級別,調(diào)整數(shù)據(jù)庫的隔離級別,由此保持一致
    2022-11-11
  • 在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解

    在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解

    這篇文章主要介紹了在java8中使用流區(qū)分質(zhì)數(shù)與非質(zhì)數(shù)詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 如何將Spring Session存儲到Redis中實(shí)現(xiàn)持久化

    如何將Spring Session存儲到Redis中實(shí)現(xiàn)持久化

    這篇文章主要介紹了如何將Spring Session存儲到Redis中實(shí)現(xiàn)持久化,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • Java下載文件中文文件名亂碼的解決方案(文件名包含很多%)

    Java下載文件中文文件名亂碼的解決方案(文件名包含很多%)

    Java下載文件時,文件名中文亂碼問題通常是由于編碼不正確導(dǎo)致的,使用`URLEncoder.encode(filepath, "UTF-8")`可以解決在提示下載框中正確顯示漢字文件名的問題,但在選擇直接打開時,文件名會變成亂碼,解決這個問題的方法
    2025-02-02
  • Java裝飾者模式的示例詳解

    Java裝飾者模式的示例詳解

    裝飾者模式:在不改變原有對象的基礎(chǔ)之上,動態(tài)的將功能附加到對象上,提供了繼承更有彈性的替代方案,也體現(xiàn)了開閉原則。本文將通過示例詳細(xì)講解一下裝飾者模式,需要的可以參考一下
    2022-02-02
  • java并發(fā)編程之深入理解Synchronized的使用

    java并發(fā)編程之深入理解Synchronized的使用

    文詳細(xì)講述了線程、進(jìn)程的關(guān)系及在操作系統(tǒng)中的表現(xiàn),這是多線程學(xué)習(xí)必須了解的基礎(chǔ)。本文將接著講一下Java線程同步中的一個重要的概念synchronized,希望能夠給你有所幫助
    2021-06-06
  • SpringBoot整合mybatis-generator插件流程詳細(xì)講解

    SpringBoot整合mybatis-generator插件流程詳細(xì)講解

    這篇文章主要介紹了SpringBoot整合mybatis-generator插件流程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • 解決java壓縮圖片透明背景變黑色的問題

    解決java壓縮圖片透明背景變黑色的問題

    這篇文章主要介紹了解決java壓縮圖片透明背景變黑色的問題,需要的朋友可以參考下
    2014-04-04
  • SpringBoot靜態(tài)視頻實(shí)時播放的實(shí)現(xiàn)代碼

    SpringBoot靜態(tài)視頻實(shí)時播放的實(shí)現(xiàn)代碼

    這篇文章主要介紹了SpringBoot靜態(tài)視頻實(shí)時播放的實(shí)現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01

最新評論

平南县| 南城县| 秦皇岛市| 泸溪县| 沙坪坝区| 杨浦区| 福安市| 龙胜| 凌海市| 河西区| 崇仁县| 大新县| 方正县| 南靖县| 临邑县| 千阳县| 景泰县| 布尔津县| 乌海市| 宁安市| 樟树市| 梨树县| 内江市| 沅陵县| 定结县| 昌图县| 临湘市| 玛纳斯县| 子洲县| 攀枝花市| 西盟| 丹巴县| 鄂州市| 湛江市| 乐安县| 高阳县| 昌都县| 崇义县| 民勤县| 新建县| 湟中县|