JVM—java類加載機制

1.java類加載機制

       1.1 java類加載機制中最重要的是類初始化過程,包含靜態資源,非靜態資源,父子類,構造方法等執行順序。

學過java的人都應該知道:帶有static關鍵字的代碼塊應該是最先被執行的,其次是非static關鍵字的代碼塊以及類的屬性,最後是構造方法;

所以類初始化過程應該是:父類的靜態代碼塊—子類的靜態代碼塊—初始化父類的屬性/父類的普通代碼塊(代碼自上而下執行)—父類的構造方法—初始化子類的屬性/子類普通代碼塊—子類的構造方法、

       1.2 類的加載(java類的生命週期):java類加載分5個過程:加載,連接(驗證,準備,解析),初始化,使用,卸載;

           1.2.1 加載:

類的加載是指:將類的.class文件中的二進制數據讀到內存中,將其放在運行時數據區的方法區內,然後再堆區創建一個java.lang.Class對象,用來封裝類在方法區的數據結構,類的加載最終的產品是位於堆區的Class對象,Class封裝了類方法區的數據結構,並向java程序猿提供了訪問方法區的數據結構的接口;

類加載器並不需要等到某個類被首次主動使用時再加載它,jvm規範允許類加載器在預料某個類將要被使用時預先加載它,如果在預先加載過程中遇到了.class文件缺失或者錯誤,類加載器必須在程序首次使用該類時才報錯,如果這個程序一直沒有被程序主動使用,那麼類加載器就不會報告這個錯誤。

換言之:加載主要是將.class文件也可以是zip包,通過二進制字節流讀入到jvm中,

a.通過classloader在classpath中獲取.class文件,將其以二進制字節流形式讀入內存;

b.將字節流所代表的靜態存儲數據結構轉換爲方法區的運行時數據結構;

c.在java堆中生成一個該類的java.lang.Class對象,作爲方法區這個類的各種數據的訪問入口;

           1.2.2 鏈接

連接—驗證:確保被加載類的正確性,驗證是連接的第一步,目的是確保Class文件的字節流中包含的信息符合當前虛機的要求,不會危害虛機自身的安全,

文件格式驗證:驗證字節流中是否符合class文件格式規範,主次版本號是否在當前虛擬機的處理範圍,常量池中的常量是否有不被支持的類型等

元數據驗證:對字節碼描述的信息進行語義分析,保證其描述的信息符合java語言規範的要求,

字節碼驗證:通過字節流和控制流分析,確保程序語義是合法的,符合邏輯的。

符號引用驗證:確保解析動作能正確執行

連接—準備:爲類的靜態變量分配內存,並將其初始化爲默認值:正式爲類變量分配內存並設置類變量初始化的階段,這些內存都將在方法區中分配(內存分配的僅包含類變量[static],而不包含實例變量,實例變量會在對象實例化時隨着對象一塊分配在java堆中。初始化值通常情況下是數據類型默認的零值【如0,0L,null,false等】,而不是被在java代碼中顯示賦值)

鏈接—解析:把類中的符號引號轉換爲直接引用:虛擬機將常量池內的符號引用替換爲直接引用的過程,解析動作主要針對類或接口,字段,類方法,接口方法,方法類型,方法句柄和調用點限定符7類符號引用進行,符號引用就是一組符號來描述目標,可以是任何字面量。

           1.2.3 初始化:爲類的靜態變量賦予正確的初始值,jvm負責對類進行初始化,主要對類變量進行初始化,在java中對類變量進行初始值設定有兩種方式:a.聲明類變量是指定初始值,b.靜態代碼塊爲類變量指定初始值;

jvm初始化:

a. 假如這個類還沒有被加載和連接,則程序先加載並連接該類

b. 假如該類的直接父類還沒有被初始化,則先初始化其直接父類

c. 假如類中有初始化語句,則系統依次執行這些初始化語句

類初始化時機:只有當對類的主動使用的時候纔會導致類的初始化,類的主動使用包含:創建類的實例(new);訪問某個類或接口的靜態變量,或者對該靜態變量賦值;調用類的靜態方法;反射(Class.froNname("全限定名"));初始化某個類的子類,父類也會被初始化;java虛擬機啓動時被標記爲啓動類的類,直接使用java.exe命令運行某個主類。

java虛擬機將結束生命週期:執行了system.exit()方法;程序正常執行結束;程序在執行過程中遇到了異常或者錯誤終止;由於操作系統出現錯誤導致java虛擬機進程終止;

       1.3類加載器

 

這裏的父類加載器不是通過繼承關係實現,而是採用組合實現的;在java虛擬機角度來講,只有兩種不同的加載器:啓動類加載器(使用c++實現Hostpot,JDK1.5之後默認的虛擬機,有很多其他虛擬機是用java語言實現的),是虛擬機自身的一部分;所有其他類的加載器:這些類加載器都由java語言實現的,獨立於虛擬機之外,並且全部繼承自抽象類java.lang.ClassLoader,這些類加載器需要由啓動類加載器加載到內存之中才能加載其他的類;

啓動類加載器:Bootstrap ClassLoader,負責加載存放在JDKPATH/jre/lib下,或者-Xvootclasspath參數指定的路徑中,並且能被虛擬機識別的類庫中,啓動類加載器是無法被java程序直接引用的;

擴展類加載器:Extension ClassLoader,這個加載器由sun.misc.Launcher$AppClassLoader來實現,它負責加載jdkpath/jre/lib/ext目錄中,或者由java.ext.dirs系統變量指定的路徑中所有的類庫,開發者可以直接使用擴展類加載器

應用程序類加載器:Application ClassLoader,該類加載器由sun.misc.Launcher$AppClassLoader來實現,他負責加載用戶類路徑所指定的類,開發者可以直接使用該類加載器,如果應用程序中沒有自定義自己的類加載器,一般情況下這個就是程序中默認的類加載器

2. jvm類加載機制

全盤負責:當一個類加載器負責加載某個class時,改class所依賴的和引用的其他class也將由該類加載器載入,除非顯示使用另外一個類加載器來載入

父類委託:先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類

緩存機制:緩存機制將會保證所有加載過的class都會被緩存,當程序中需要某個class時,類加載器先從緩存區尋找該class,只有緩存區不存在,系統纔會讀取該類對應的二進制數據,並將其轉換成class對象,存入緩存區,這也就是每次爲啥修改了class後,必須重啓jvm,程序修改纔會生效;

3.類的加載

       3.1 類加載方式:

命令行啓動應用時jvm初始化加載;通過class.forName()方法動態加載;通過classLoader.loaderClass()方法動態加載

       3.2 類加載方式區別

          3.2.1Class.forName()和ClassLoader()區別

Class.forName():將類的.class文件加載到jvm中之外,還會對類進行解釋,執行類中的static快;

ClassLoader.loaderClass():將.class文件加載到jvm中,不會執行static中的內容,只有在newInstance纔會去執行static快;Class.forName(name,initialize,loader)帶參函數也可以控制是否加載static快,並且只有調用newInstace()方法採用調用構造函數,創建類對象

4. 雙親委託模型:雙親委託模型的工作流程:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把請求委託給父加載器去完成,依次向上,因此,所有的類加載請求最終都應該被傳遞到頂層的啓動類加載器中,只有當父加載器在它的搜索範圍中沒有找到所需要的類時,即無法完成該加載,子加載器纔會嘗試自己去加載該類。

雙親委託機制(系統類防止內存中出現多份同樣的字節碼;保證java程序安全穩定運行)

當AppClassLoader加載一個class時,它首先不會自己去嘗試加載這個類,而是把這個類加載請求委託父類加載器ExtClassLoader去完成;

當ExtClassLoader加載一個class時,它首先不會自己去嘗試加載這個類,而是把類加載請求委託給BootStrapClassLoader去完成

如果BootStrapClassLoader加載失敗,會使用ExtClassLoader類嘗試加載

若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,如果使用AppClassLoader也加載失敗,則會報出異常ClassnNotFoundException.

 

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