類的加載機制是指把編譯後的.class類文件的二進位數據讀取到內存中,並為之創建一個java.lang.Class對象,用來封裝類在元數據空間的數據結構。
類在JVM中的生命周期為:加載,連接,初始化,使用,卸載。不過這裡只重點描述加載,連接,初始化這三個過程。
基於一張圖看懂加載子系統的細節流程:
過程描述
加載階段需要完成以下三個過程:
類加載器
Bootstrap-ClassLoader基於C/C++實現,負責加載Java的核心類庫JAVA_HOME\jre\lib\rt.jar,該加載器不繼承自ClassLoader抽象類,並且只加載包名為java、javax、sun等開頭類,一次保證對核心源碼的保護。
Extension-ClassLoader,基於Java語言,由sun.misc.Launcher$ExtClassLoader實現,派生於ClassLoader抽象類,從java.ext.dirs系統變量指定的路徑中的加載類庫,或者JDK安裝目錄jre\lib\ext目錄下加載。
Application-ClassLoader,基於Java語言,由sun.misc.Launcher$ExtClassLoader實現,它負責加載環境變量ClassPath指定的類庫,如果在應用程式中沒有自定義類加載器,一般情況下作為程序中默認的類加載器。
驗證
目的在於確保Class文件的字節流中包含的信息符合當前虛擬機的要求,保證加載類的正確性,不會危害虛擬機自身的安全,主要包括四種檢驗動作:
準備
為類的靜態變量分配內存,並初始化為默認值,這時候進行內存分配的僅包括類變量(static)修飾,不包括(final-static)修飾的,這裡也不會為實例變量分配初始化,實例變量會隨著對象一塊分配到Java堆中。
解析
將常量池中的符號引用轉換為直接引用的過程,直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄。解析主要針對類或接口、欄位、類方法、接口方法、方法類型等,解析的動作實際是會隨著JVM在執行完初始化之後再執行的。
執行類構造器clinit()方法的過程,該方法不需要自定義,是javac編譯器自動收集類中的所有類變量的賦值動作和靜態代碼塊中的語句合併而來,Jvm要保證clinit()方法在多線程訪問下的安全性。
類加載器收到了類加載的請求時,不會自己先去嘗試加載這個類,而是把請求委託給父加載器去執行;
如果父加載器還存在父類加載器,則依次向上委託,因此類加載請求最終都應該被傳遞到頂層的啟動類加載器中;
如果父類加載器可以完成類加載請求,就直接成功返回,只有當父加載器在無法完成該加載,子加載器才會嘗試自己去加載該類;
假設自定義一個類名為String且所在包為java.lang,在使用引導類加載器加載時會先加載JDK中的String類,因為這個類本來是屬於jdk的,後面再次出現String類就會報錯,以此保證原始碼不被惡意篡改,這就是沙箱安全機制。