虛擬機類加載機制java
虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成可使用的java類型程序員
類加載的時機數組
類整個生命週期:加載、驗證、準備、解析、初始化、使用和卸載數據結構
初始化階段,有且只有5種狀況必須進行初始化多線程
1 遇到new、getstatic、putstatic或invokestatic這4個字節碼指令時,若是類沒有進行初始化,須要先初始化spa
2 使用java.lang.reflect包的方法對類進行反射調用的時候,若是類沒有初始化,須要先初始化線程
3 一個類初始化的時候,若是發現父類沒有初始化,須要父類先初始化指針
4 當虛擬機啓動時,用戶須要指定一個要執行的主類(包含main()方法的那個類)。虛擬機會先初始化這個類對象
5 當使用JDK1.7的動態語言支持時,若是一個java.lang.invoke.MethodHandle實例最後的解析結果繼承
REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,且這個方法句柄所對應的類沒有初始化,須要初始化
類加載的過程
加載
1 經過一個類的全限定名來獲取定義此類的二進制字節流
2 將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構
3 在內存中生成一個表明這個類的java.lang.Class對象,做爲方法區這個類的各類數據的訪問入口
一個非數組類的加載階段便可以使用系統提供的引導類加載器來完成,也能夠由用戶自定義的類加載器去完成
一個數組類不經過類加載器建立,由java虛擬機直接建立,遵循如下規則:
1 若是數組的項是引用類型,那就遞歸加載這個類型,
2 若是數組的項不是引用類型,java虛擬機會把數組標記爲與引導類加載器關聯
3 數組類的可見性與它的項的可見性一致,若是項類型不是引用類型,那數組類的可見性將默認爲public
加載完成後,虛擬機外部的二進制字節流就按照虛擬機所需的格式存儲在方法區
驗證
驗證是鏈接階段的第一步,爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,不會危害虛擬機
1 文件格式驗證
驗證字節流是否符合Class文件格式的規範,而且能被當前版本的虛擬機處理
2 元數據驗證
對字節碼描述的信息進行語義分析,以保證其描述的信息符合java語言規範的要求
3 字節碼驗證
經過數據流和控制流分析,肯定程序語義是合法的、符合邏輯的
4 符號引用驗證
對類自身之外的信息進行匹配性校驗
準備
準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些變量所使用的內存都將在方法區中進行分配
這時候進行內存分配的僅包括類變量(被static修飾的變量),而不包括實例變量
解析
解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程
符號引用:
符號引用以一組符號來描述所引用的目標,符號能夠是任何形式的字面量,只要能無歧義定位到目標便可
直接引用:
能夠是直接指向目標的指針、相對偏移量或一個能間接定位到目標的句柄
初始化
根據程序員經過程序指定的主觀計劃去初始化類變量和其餘資源,執行類構造器<clinit>()方法的過程
<clinit>()特色:
1 方法由編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊中的語句合併產生的
2 不須要顯式地調用父類構造器
3 父類中的靜態語句塊要優先於子類的變量賦值操做
4 對於類或接口來講不是必需的
5 接口與類都會生成此方法
6 虛擬機會保證一個類的<clinit>方法在多線程中被正確的加鎖、同步
類加載器
虛擬機把類加載中的「經過一個類的全限定名來獲取描述此類的二進制字節流」放到java虛擬機外部去實現
以便讓程序本身決定如何去獲取所須要的類,實現這個動做的代碼模塊稱爲"類加載器"
類與類加載器
任何一個類,都須要由加載它的類加載器和這個類自己肯定其在java虛擬機中的惟一性
每個類加載器,都擁有一個獨立的類名稱空間
雙親委派模型
從java虛擬機的角度講,只存在兩種不一樣的類加載器:
1 啓動類加載器,C++實現,是虛擬機的一部分
2 其餘的類加載器,由java實現,獨立於虛擬機外部,全都繼承自java.lang.ClassLoader
從開發人員的角度看,有3種系統提供的類加載器
1 啓動類加載器,將存放在<JAVA_HOME>\lib目錄中,或者被-Xbootclasspath參數所指定的路徑中
而且是虛擬機識別的類庫加載到虛擬內存中
2 擴展類加載器,加載<JAVA_HOME>\lib\ext,或者被java.ext.dirs系統變量所指定的路徑中的全部類庫
開發者能夠直接使用擴展類加載器
3 應用程序類加載器,通常稱爲系統類加載器,負責加載用戶類路徑所指定的類庫,開發者能夠直接使用
這個就是程序中默認的類加載器
雙親委派模型的工做過程:
若是一個類加載器收到類加載的請求,它首先不會本身去嘗試加載這個類,而是把這個請求委派給父類加載器
只有父加載器反饋本身沒法完成這個加載請求時,子加載器纔會嘗試本身加載