亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JVM虛擬機(jī)的類加載機(jī)制詳解

 更新時(shí)間:2023年12月19日 09:47:11   作者:初念初戀  
這篇文章主要介紹了JVM虛擬機(jī)的類加載機(jī)制詳解,類是在運(yùn)行期間第一次使用時(shí)動(dòng)態(tài)加載的,而不是一次性加載所有類,因?yàn)槿绻淮涡约虞d,那么會(huì)占用很多的內(nèi)存,需要的朋友可以參考下

一、類的生命周期

類是在運(yùn)行期間第一次使用時(shí)動(dòng)態(tài)加載的,而不是一次性加載所有類。因?yàn)槿绻淮涡约虞d,那么會(huì)占用很多的內(nèi)存。

11.png

包括以下 7 個(gè)階段:

  • 加載(Loading)
  • 驗(yàn)證(Verification)
  • 準(zhǔn)備(Preparation)
  • 解析(Resolution)
  • 初始化(Initialization)
  • 使用(Using)
  • 卸載(Unloading)

二、類的加載過程

包含了加載、驗(yàn)證、準(zhǔn)備、解析和初始化這 5 個(gè)階段。

加載

加載過程完成以下三件事:

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

其中二進(jìn)制字節(jié)流可以從以下方式中獲?。?/p>

  • 從 ZIP 包讀取,成為 JAR、EAR、WAR 格式的基礎(chǔ)。
  • 從網(wǎng)絡(luò)中獲取,最典型的應(yīng)用是 Applet。
  • 運(yùn)行時(shí)計(jì)算生成,例如動(dòng)態(tài)代理技術(shù),在 java.lang.reflect.Proxy 使用 ProxyGenerator.generateProxyClass 的代理類的二進(jìn)制字節(jié)流。
  • 由其他文件生成,例如由 JSP 文件生成對(duì)應(yīng)的 Class 類。

驗(yàn)證

JVM 會(huì)在該階段對(duì)二進(jìn)制字節(jié)流進(jìn)行校驗(yàn),只有符合 JVM 字節(jié)碼規(guī)范的才能被 JVM 正確執(zhí)行。該階段是保證 JVM 安全的重要屏障,下面是一些主要的檢查。

  • 確保二進(jìn)制字節(jié)流格式符合預(yù)期(比如說是否以 cafe bene 開頭)。
  • 是否所有方法都遵守訪問控制關(guān)鍵字的限定。
  • 方法調(diào)用的參數(shù)個(gè)數(shù)和類型是否正確。
  • 確保變量在使用之前被正確初始化了。
  • 檢查變量是否被賦予恰當(dāng)類型的值。

準(zhǔn)備

JVM 會(huì)在該階段對(duì)類變量(也稱為靜態(tài)變量,static 關(guān)鍵字修飾的)分配內(nèi)存并初始化(對(duì)應(yīng)數(shù)據(jù)類型的默認(rèn)初始值,如 0、0L、null、false 等)。

此時(shí)不會(huì)分配實(shí)例變量的內(nèi)存,因?yàn)閷?shí)例變量是在實(shí)例化對(duì)象時(shí)一起創(chuàng)建在Java 堆中的。而且此時(shí)類變量是賦值為零值,即 int 類型的零值為 0,引用類型零值為 null,而不是代碼中顯示賦值的數(shù)值。

解析

該階段將常量池中的符號(hào)引用轉(zhuǎn)化為直接引用。

在 class 文件中常量池里面存放了字面量和符號(hào)引用,符號(hào)引用包括類和接口的全限定名以及字段和方法的名稱與描述符。

在 JVM 動(dòng)態(tài)鏈接的時(shí)候需要根據(jù)這些符號(hào)引用來轉(zhuǎn)換為直接引用存放內(nèi)存使用。

初始化

該階段是類加載過程的最后一步。在準(zhǔn)備階段,類變量已經(jīng)被賦過默認(rèn)初始值,而在初始化階段,類變量將被賦值為代碼期望賦的值。換句話說,初始化階段是執(zhí)行類構(gòu)造器方法的過程。

三、類加載時(shí)機(jī)

  • new、getstatic、putstatic、invokestatic 這 4 個(gè)字節(jié)碼指令時(shí)對(duì)類進(jìn)行初始化(即:實(shí)例化對(duì)象、讀寫靜態(tài)對(duì)象、調(diào)用靜態(tài)方法時(shí),進(jìn)行類的初始化);
  • 使用反射機(jī)制對(duì)類進(jìn)行調(diào)用時(shí),進(jìn)行類的初始化;
  • 初始化一個(gè)類,其父類沒有初始化時(shí),先初始化其父類;
  • 虛擬機(jī)啟動(dòng)時(shí),初始化一個(gè)執(zhí)行主類;
  • 使用 JDK1.7 的動(dòng)態(tài)語言支持時(shí),如果 MethodHandle 實(shí)例的解析結(jié)果為 REF_getstatic、REF_putstatic、REF_invokestatic 的方法句柄(即:讀寫靜態(tài)對(duì)象或者調(diào)用靜態(tài)方法),則初始化該句柄對(duì)應(yīng)類。

四、類加載器分類

講到類加載不得不講到類加載的順序和類加載器。

Java 中大概有四種類加載器,分別是:啟動(dòng)類加載器(Bootstrap ClassLoader),擴(kuò)展類加載器(Extension ClassLoader),系統(tǒng)類加載器(System ClassLoader),自定義類加載器(Custom ClassLoader),依次屬于繼承關(guān)系(注意這里的繼承不是 Java 類里面的 extends)

classloader2.jpg

  • 啟動(dòng)類加載器(Bootstrap ClassLoader):主要負(fù)責(zé)加載存放在Java_Home/jre/lib下,或被-Xbootclasspath參數(shù)指定的路徑下的,并且能被虛擬機(jī)識(shí)別的類庫(如rt.jar,所有的java.*開頭的類均被Bootstrap ClassLoader加載),啟動(dòng)類加載器是無法被Java程序直接引用的。
  • 擴(kuò)展類加載器(Extension ClassLoader):主要負(fù)責(zé)加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載Java_Home/jre/lib/ext目錄中,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器。
  • 系統(tǒng)類加載器(System ClassLoader):主要負(fù)責(zé)加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn),它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類,開發(fā)者可以直接使用該類加載器,如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器。
  • 自定義類加載器(Custom ClassLoader:自己開發(fā)的類加載器。

五、雙親委派原則

類加載器在加載 class 文件的時(shí)候,遵從雙親委派原則,意思是加載依次由父加載器先執(zhí)行加載動(dòng)作,只有當(dāng)父加載器沒有加載到 class 文件時(shí)才由子類加載器進(jìn)行加載。這種機(jī)制很好的保證了 Java API 的安全性,使得 JDK 的代碼不會(huì)被篡改。

六、Java字節(jié)碼文件中的JVM指令

1、創(chuàng)建一個(gè) Java 源文件 Test02.java,并在 main 方法中完成簡單的邏輯操作,如下所示。

public class Test02 {
    public static void main(String[] args) {
        int i = 5;
        int j = 10;
        int k = i + j;
        System.out.println(k);
    }
}

2、在終端通過 javac 命令編譯 HelloWorld.java。

javac Test02.java

3、反編譯成我們能看懂的 JVM 指令,這里我們使用 javap -c 命令完成。

javap -c Test02.class

4、反編譯之后的 JVM 指令如下所示。

    Compiled from "Test02.java"
    public class org.example.jvm.Test02 {
     public org.example.jvm.Test02();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
      public static void main(java.lang.String[]);
         Code:
           0: iconst_5
           1: istore_1
           2: bipush        10
           4: istore_2
           5: iload_1
           6: iload_2
           7: iadd
           8: istore_3
           9: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
          12: iload_3
          13: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
          16: return
    }

解釋一下上述的 JVM 指令:

第 1 行表示當(dāng)前的字節(jié)碼文件編譯自 Test02.java。

第 3 行表示調(diào)用 Test02的無參構(gòu)造函數(shù)來實(shí)例化當(dāng)前對(duì)象。

第 4 行到第 7 行表示無參構(gòu)造函數(shù)的執(zhí)行流程。

第 5 行表示把 this 壓入操作數(shù)棧中。

第 6 行表示調(diào)用 Test02父類 Object 的無參構(gòu)造,我們知道每個(gè)對(duì)象在實(shí)例化的時(shí)候都會(huì)默認(rèn)先實(shí)例化其父類對(duì)象,并且默認(rèn)調(diào)用父類的無參構(gòu)造。

第 7 行 return 表示構(gòu)造方法執(zhí)行完畢。

第 10 行到第 22 行表示 main 方法的執(zhí)行流程。

第 11 行表示將常量 5 壓入操作數(shù)棧。

第 12 行表示取出操作數(shù)棧棧頂元素,即 5,然后保存到局部變量表第 1 個(gè)位置,即變量 i。

第 13 行表示將常量 10 壓入操作數(shù)棧。

第 14 行表示取出操作數(shù)棧棧頂元素,即 10,然后保存到局部變量表第 2 個(gè)位置,即變量 j。

第 15 行表示將局部變量表第 1 個(gè)變量(i)壓入操作數(shù)棧。

第 16 行表示將局部變量表第 2 個(gè)變量(j)壓入操作數(shù)棧。

第 17 行表示取出操作數(shù)棧中的前兩個(gè)值相加,并將結(jié)果壓入操作數(shù)棧頂。

第 18 行表示取出操作數(shù)棧棧頂元素,保存到局部變量表第 3 個(gè)位置,即變量 k。

第 19 行表示讀取靜態(tài)實(shí)例 PrintStream。

第 20 行表示將局部變量表第 3 個(gè)變量(k)壓入操作數(shù)棧。

第 21 行表示調(diào)用 PrintStream 的 println 方法,將操作數(shù)棧頂元素(變量 k)輸出。

第 22 行 return 表示 main 方法執(zhí)行完畢。

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

相關(guān)文章

最新評(píng)論