1.概述java
a.JVM類加載機制:是虛擬機把描述類的數據從Class
文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成可被虛擬機直接使用的Java
類型的過程。數據庫
b.特性:運行期類加載。即在Java語言裏面,類型的加載、鏈接和初始化過程都是在程序運行期完成的,從而經過犧牲一些性能開銷來換取Java程序的高度靈活性。數組
JVM運行期動態加載+動態鏈接->Java語言的動態擴展特性安全
2.類加載全過程網絡
類從被加載到虛擬機內存中開始、到卸載出內存爲止,整個生命週期包括7階段:數據結構
其中,驗證、準備、解析這3個部分統稱爲鏈接(Linking),流程以下圖:多線程
注意:佈局
- 『加載』->『驗證』->『準備』->『初始化』->『卸載』這5個階段的順序是肯定的,而『解析』可能爲了支持Java語言的運行時綁定會在『初始化』後纔開始。
- 上述階段一般都是互相交叉地混合式進行的,好比會在一個階段執行的過程當中調用、激活另一個階段。
接下來將分別介紹上述幾個階段。性能
a.加載spa
任務:
- 經過類的全限定名來獲取定義此類的二進制字節流。如從ZIP包讀取、從網絡中獲取、經過運行時計算生成、由其餘文件生成、從數據庫中讀取等等途徑......
- 將該二進制字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構,該數據存儲數據結構由虛擬機實現自行定義。
- 在內存中生成一個表明這個類的
java.lang.Class
對象,它將做爲程序訪問方法區中的這些類型數據的外部接口。
b.驗證
因而可知,它能直接決定JVM可否承受惡意代碼的攻擊,所以驗證階段頗有必要,但因爲它對程序運行期沒有影響,並不必定必要,能夠考慮使用
-Xverify:none
參數來關閉大部分的類驗證措施,以縮短虛擬機類加載的時間。
java.lang.IncompatibleClassChangeError
異常的子類。c.準備
任務:
- 爲類變量分配內存:由於這裏的變量是由方法區分配內存的,因此僅包括類變量而不包括實例變量,後者將會在對象實例化時隨着對象一塊兒分配在Java堆中。
- 設置類變量初始值:一般狀況下零值。
d.解析
CONSTANT_Class_info
)CONSTANT_Fieldref_info
)CONSTANT_Methodref_info
)CONSTANT_InterfaceMethodref_info
)CONSTANT_MethodType_info
)CONSTANT_MethodHandle_info
)CONSTANT_InvokeDynamic_info
)舉個例子,設當前代碼所處的爲類D,把一個從未解析過的符號引用N解析爲一個類或接口C的直接引用,解析過程分三步:
- 若C不是數組類型:JVM將會把表明N的全限定名傳遞給D類加載器去加載這個類C。在加載過程當中,因爲元數據驗證、字節碼驗證的須要,又可能觸發其餘相關類的加載動做。一旦這個加載過程出現了任何異常,解析過程就宣告失敗。
- 若C是數組類型且數組元素類型爲對象:JVM也會按照上述規則加載數組元素類型。
- 若上述步驟無任何異常:此時C在JVM中已成爲一個有效的類或接口,但在解析完成前還需進行符號引用驗證,來確認D是否具有對C的訪問權限。若是發現不具有訪問權限,將拋出
java.lang.IllegalAccessError
異常。
e.初始化
<clinit>()
的過程。
<clinit>()
:由編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊static{}
中的語句合併產生。
- 是線程安全的,在多線程環境中被正確地加鎖、同步。
- 對於類或接口來講是非必需的,若是一個類中沒有靜態語句塊,也沒有對變量的賦值操做,那麼編譯器能夠不爲這個類生成
<clinit>()
。- 接口與類不一樣的是,執行接口的
<clinit>()
不須要先執行父接口的<clinit>()
,只有當父接口中定義的變量使用時,父接口才會初始化。另外,接口的實現類在初始化時也同樣不會執行接口的<clinit>()
。
new
、getstatic
、putstatic
或invokestatic
這4條字節碼指令時;java.lang.reflect
包的方法對類進行反射調用的時候;java.lang.invoke.MethodHandle
實例最後的解析結果爲REF_getStatic
、REF_putStatic
、REF_invokeStatic
的方法句柄,且這個方法句柄所對應的類未進行初始化,需先觸發其初始化。3.類加載器&雙親委派模型
每一個類加載器都擁有一個獨立的類名稱空間,它不只用於加載類,還和這個類自己一塊兒做爲在JVM中的惟一標識。因此比較兩個類是否相等,只要看它們是否由同一個類加載器加載,即便它們來源於同一個Class文件且被同一個JVM加載,只要加載它們的類加載器不一樣,這兩個類就一定不相等。
a.類加載器
從JVM的角度,可將類加載器分爲兩種:
C++
語言實現,是虛擬機自身的一部分。<JAVA_HOME>\lib
目錄中、或被-Xbootclasspath
參數所指定路徑中的、且可被虛擬機識別的類庫。Java
語言實現,獨立於虛擬機外部,而且全都繼承自抽象類java.lang.ClassLoader
,可被Java程序直接引用。常見幾種:
sun.misc.Launcher$ExtClassLoader
實現。<JAVA_HOME>\lib\ext
目錄中的、或者被java.ext.dirs
系統變量所指定的路徑中的全部類庫。ClassLoader#getSystemClassLoader()
的返回值,故又稱爲系統類加載器。sun.misc.Launcher$App-ClassLoader
實現。上述幾種類加載器的關係如圖:
須要注意的是,雖然數組類不經過類加載器建立而是由JVM直接建立的,但仍與類加載器有密切關係,由於數組類的元素類型最終還要靠類加載器去建立。
b.雙親委派模型(Parents Delegation Model)
java.lang.ClassLoader的loadClass()
中。好比,某些類加載器要加載
java.lang.Object
類,最終都會委派給最頂端的啓動類加載器去加載,這樣Object
類在程序的各類類加載器環境中都是同一個類。相反,系統中將會出現多個不一樣的Object類,Java類型體系中最基礎的行爲也就沒法保證,應用程序也將會變得一片混亂。