深入理解java虛擬機類加載機制,通俗易懂

java源文件經編譯後以.class的文件形式存在本地磁盤上,在Class文件中描述的各類信息最終都需要加載到虛擬機中之後才能被運行和使用

類加載機制在jvm中的位置

在這裏插入圖片描述

1.類加載過程

虛擬機把類的數據從.class文件加載到內存,並對class文件中的數據進行校驗、轉換、解析、初始化等操作後,最終形成可以被虛擬機識別並使用的Class對象的過程就叫做“虛擬機的類加載”,主要包括爲3大階段。在這裏插入圖片描述

加載->鏈接->初始化三大階段,這三個階段也被稱成爲類加載子系統

其中鏈接包含了驗證,準備,解析三個子階段,下面根據這三大階段一一解釋

1.1加載

需要注意的是,這裏的加載是整個類加載子系統的一個小階段,在這個階段,java虛擬機需要完成以下三件事:

  • 通過一個類的全限定類名獲取定義此類的二進制字節流
  • 將這個字節流所代表的靜態存儲結構轉化爲方法區的運行時數據結構
  • 在內存中生成一個代表這個類的java.lang.Class對象,作爲方法區這個類的各種數據的訪問入口

1.2驗證

確保class文件符合java虛擬機要求

1.3準備

簡單的來說爲被static修飾的變量賦初始值,分配內存,這些變量所使用的內存在方法區進行分配,JKD7之前虛擬機將其存放於永久代JKD8及以後,類變量會隨着Class對象一起放在堆中,實例變量會在對象實例化時隨對象一起分配到堆中

1.4解析

這裏不贅述

1.5初始化

這是類加載過程的最後一步,在之前的準備階段,類變量已經賦初始值了,這裏的初始化就是虛擬機根據程序員書寫靜態變量,靜態代碼塊的順序執行賦值等動作,但大家需要注意以下注意點

public class Demo {
    public static void main(String[] args) {
        System.out.println(p.i);
    }
}
class p{
    {
        i = 2;
        
    }
    static int i = 1;
}

這段代碼在編譯器是不會報錯的,但是如果在代碼塊中執行System.out.println(i);就會報錯:超前引用,即可以賦值,但不可訪問

2.類加載器

上面的架構圖已經揭示了類加載器的作用,類加載器類加載器(class loader)將Java類從本地磁盤加載到Java虛擬機中,並同時創建了該類的Class對象,實現了“通過一個類的全限定類名來獲取此類的二進制字節流”功能。通過一個例子將類加載器和類加載過程做一個說明
在這裏插入圖片描述
一個car類在被編譯後形成了class文件,被類加載器加載到jvm中並分配了內存和唯一一個class對象,通過這個class對象就可以在堆中實例化多個car實例對象

2.1類加載器的分類

java虛擬機中,只存在兩種不同的類加載器:

  • 啓動類加載器Bootstrap Classloader,由C++編寫,屬於虛擬機一部分
  • 繼承自抽象類java.lang.Classloader的類加載器,由Java語言編寫,獨立於虛擬機之外

現在可能有人會問獨立於虛擬機之外是什麼意思了,請往下看:

2.1.1 啓動類加載器

它只負責加載你在環境變量中配置的<JAVA_HOME>\lib下的內容,由於是C++編寫,不能被java程序所引用,用以下代碼演示

public class Demo {
    public static void main(String[] args) {
        System.out.println(Demo.class.getClassLoader().getParent().getParent());
    }
}

當用戶使用以上方式想要獲取Bootstrap Classloader是會失敗的,返回的是null;

2.1.2 繼承自抽象類java.lang.Classloader的類加載器
  • Extension Class Loader,擴展類加載器
  • Application Class Loader(System Class Loader),應用程序類加載器

這兩個類都在sun.misc.Launcher類中,屬於內部類,不同的是 Extension Class Loader只會加載<JAVA_HOME>\lib\ext目錄中的內容,而 Application Class Loader則會加載我們自定義的類
展示一下他們的繼承關係
在這裏插入圖片描述

需要提一下sun.misc.Launcher,這是java虛擬機的入口程序,再次之前我們先要了解下虛擬機啓動過程:
如何啓動?java 命令

java命令是一個入口,執行的時候 會找到對應的執行文件,它會調用java api 接口(java api 接口和jvm 一起構成了jre),接口內部會調用其他接口創建虛擬機(虛擬內存,硬件,CPU等),

然後虛擬機會自動創建bootstrap 類加載器,我們再來看一下Launcher
在這裏插入圖片描述
我們發現這個類位於rt.jar包下,而rt.jar這個包由Bootstrap Classloader負責加載,那麼就可以說明引導類和擴展類加載器都是由Bootstrap Classloader負責創建,到現在我們基本可以回答出剛纔的問題了:
獨立於虛擬機之外是什麼意思:擴展類加載器和程序加載器這兩個類加載器存在於jar包,要依靠虛擬機創建的啓動類加載器而創建的類加載器

3.雙親委派機制

bootstrap類加載器,會創建 擴展類加載器和應用程序類加載器,他們執行原理爲:
*

java 虛擬機執行過程

文章寫到這裏基本上將類加載機制介紹完了,從頭梳理一下jvm的執行過程

  • 1.java命令是一個入口,一般都是通過java -jar的命令啓動, 會找到對應的執行文件,它會調用java api 接口(java api 接口和jvm 一起構成了jre),接口內部會調用其他接口創建虛擬機(虛擬內存,硬件,CPU等)
  • 然後虛擬機會創建 bootstrap 類加載器,bootstrap類加載器,會創建 擴展類加載器和應用程序類加載器
  • 應用程序加載器 會首先會去尋找 方法 public static void main(String ars[]) 作爲項目的執行入口
  • 同時加載主函數所在的類,如果發現需要加載其他類,遞歸交由父類加載器加載,如果父類加載器能夠加載就加載,不能就遞歸向下交給下一級加載器加載(一般來說:bootstrap 類加載器 會加載java API 類,輔助jvm運行,擴展類加載器會加載 jdk 的類庫。應用程序加載器 會加載項目下的class文件)
  • 主函數所在的類及相關類加載完畢後,加載的結果就是 class文件,被加載的內存的方法區。
  • 接下來就是驗證,準備,解析,初始化等一系列操作,直到這個類生命週期結束被卸載

附錄

文章還會持續更新和改正
參考文章:
java虛擬機執行過程
classloader詳解

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章