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