Java 語言是一種具有動態性的解釋型語言,類(Class)只有被加載到 JVM 后才能運行。當運行指定程序時,JVM 會將編譯生成的 .class 文件按照需求和一定的規則加載到內存中,並組織成為一個完整的 Java 應用程序。這個加載過程是由類加載器完成,具體來說,就是由 ClassLoader 和它的子類來實現的。
類加載器本身也是一個類,其實質是把類文件從硬盤讀取到內存中。類的加載方式分為隱式加載和顯示加載。隱式加載指的是程序在使用 new 等方式創建對象時,會隱式地調用類的加載器把對應的類加載到 JVM 中。顯示加載指的是通過直接調用 class.forName() 方法來把所需的類加載到 JVM中。
任何一個工程項目都是由許多類組成的,當程序啟動時,只把需要的類加載到 JVM 中,其他類只有被使用到的時候才會被加載,采用這種方法一方面可以加快加載速度,另一方面可以節約程序運行時對內存的開銷。此外,在 Java 語言中,每個類或接口都對應一個 .class 文件,這些文件可以被看成是一個個可以被動態加載的單元,因此當只有部分類被修改時,只需要重新編譯變化的類即可,而不需要重新編譯所有文件,因此加快了編譯速度。
在 Java 語言中,類的加載是動態的,它並不會一次性將所有類全部加載后再運行,而是保證程序運行的基礎類(例如基類)完全加載到 JVM中,至於其他類,則在需要的時候才加載。
類加載的主要步驟:
1. 加載
加載是類加載的第一個過程,在這個階段,將完成一下三件事情:
通過一個類的全限定名獲取該類的二進制流。
將該二進制流中的靜態存儲結構轉化為方法去運行時數據結構。
在內存中生成該類的 Class 對象,作為該類的數據訪問入口。
2. 驗證
驗證的目的是為了確保 Class 文件的字節流中的信息不回危害到虛擬機.在該階段主要完成以下四鍾驗證:
文件格式驗證:驗證字節流是否符合 Class 文件的規范,如主次版本號是否在當前虛擬機范圍內,常量池中的常量是否有不被支持的類型.
元數據驗證:對字節碼描述的信息進行語義分析,如這個類是否有父類,是否集成了不被繼承的類等。
字節碼驗證:是整個驗證過程中最復雜的一個階段,通過驗證數據流和控制流的分析,確定程序語義是否正確,主要針對方法體的驗證。如:方法中的
類型轉換是否正確,跳轉指令是否正確等。
符號引用驗證:這個動作在后面的解析過程中發生,主要是為了確保解析動作能正確執行。
3. 准備,給類中的靜態變量分配存儲空間。
准備階段是為類的靜態變量分配內存並將其初始化為默認值,這些內存都將在方法區中進行分配。准備階段不分配類中的實例變量的內存,實例變量將會在對象實例化時隨着對象一起分配在 Java 堆中。
4. 解析,將符號引用轉換為直接引用(這一步可選)
該階段主要完成符號引用到直接引用的轉換動作。解析動作並不一定在初始化動作完成之前,也有可能在初始化之后。
5. 初始化。對靜態變量和靜態代碼塊執行初始化工作。
初始化時類加載的最后一步,前面的類加載過程,除了在加載階段用戶應用程序可以通過自定義類加載器參與之外,其余動作完全由虛擬機主導和控制。到了初始化階段,才真正開始執行類中定義的Java 程序代碼。
6. 使用
7. 卸載