類加載
JVM 類加載機制分爲五個部分:加載、驗證、準備、解析、初始化,如圖:
流程說明
加載
加載階段會在內存中生成一個代表該類的 java.lang.Class 對象,作爲方法區中這個類的數據入口。
可獲取加載對象的方法如下:
- 從 Class 文件獲取
- 從 zip 包中讀取(如 jar、war)
- 運行時計算生成(動態代理)
- 由其他文件生成(如 JSP)
驗證
驗證階段的主要目的是爲了確保 Class 文件的字節流中包含的信息是否符合當前虛擬機的要求,並且不會危害虛擬機的自身安全
準備
準備階段是正是爲類變量分配內存並設置類變量的初始值,即在方法區中分配這些變量所用的內存空間。
注意,此處的“初始值”根據情況而定,如下:
定義第一個類變量:public static int v1 = 100;
變量 v1
在準備階段的初始值爲 0
(在程序被編譯後,由 put static
指定進行 100
的賦值,該方法存放於類構造器 <client>
中)
定義第二個類變量:public static final int v2 = 100;
變量 v2
在編譯階段會生成 ConstantValue
屬性,虛擬機在“準備階段”根據 ConstantValue
屬性將 v2
賦值爲 100
解析
解析階段指虛擬機將常量池中的符號引用替換爲直接引用。
符號引用包括:
- CONSTANT_Class_info
- CONSTANT_Field_info
- CONSTANT_Method_info
符號引用
符號引用與虛擬機實現的佈局無關,引用的目的並一定要已經加載到內存中。各種虛擬機實現的內存佈局各不相同,但都必須符合符號引用的規範
直接引用
直接引用可以是指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。如果有直接引用,那引用的目標必定已經在內存中存在
初始化
初始化階段是類加載的最後一個階段,之前的階段中,除了在加載階段可以自定義類加載器以外,其他操作都由 JVM 主導。
到了初始化階段,纔開始真正執行類中定義的 Java 程序代碼。
類構造器 <client>
初始化階段是執行類構造器 <client>
方法(構造方法)的過程。
構造方法是編譯器自動收集類中的類變量的賦值操作和靜態語句塊中的語句合併而成。虛擬機會保證子構造方法執行之前,父類構造方法已經執行完畢。如果一個類中沒有對靜態變量賦值,且沒有靜態代碼塊,那麼編譯器可以不爲這個類生成構造方法。
以下情況不會執行類初始化:
- 通過子類引用父類的靜態字段,只會觸發父類的初始化,不會觸發子類初始化
- 定義對象數組,不會觸發初始化
- 常量在編譯期間會存入調用類的常量池中,本質上沒有直接引用定義常量的類,不會觸發定義常量的類的初始化
- 通過類名獲取
Class
對象,不會觸發類的初始化 - 通過
Class.forName()
加載指定類時,如果指定參數initialize
爲false
,則不會觸發該類的初始化 - 通過
ClassLoader
默認的loadClass()
方法,不會觸發被調用類的初始化
類加載器
虛擬機設計團隊將加載動作放到 JVM 外部實現,以便讓應用程序自行決定如何獲取所需的類。JVM 提供了 3 種類加載器。
啓動類構造器(Bootstrap ClassLoader)
負責加載 JAVA_HOME\lib
目錄中的,或通過 -Xbootclasspath
參數指定路徑中的,且被虛擬機認可的類(按文件名識別,如 rt.jar)
擴展類構造器(Extension ClassLoader)
負責加載 JAVA_HOME\lib\ext
目錄中的,或通過 java.ext.dirs
系統變量指定路徑中的類庫
應用程序類加載器(Application ClassLoader)
負責加載用戶路徑 classpath
上的類庫。
JVM 通過雙親委派模型進行類的加載,可通過繼承 java.lang.ClassLoader
實現自定義的類加載器
雙親委派
當一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父類去完成,每一個層次類加載器都是如此,因此所有的加載請求都應該傳送到啓動類加載其中,只有當父類加載器反饋自己無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的 Class),子類加載器纔會嘗試自己去加載。
採用雙親委派的一個好處是比如加載位於 rt.jar
包中的類 java.lang.Object
,不管是哪個加載器加載這個類,最都是委託給頂層的啓動類加載器進行加載,這樣就保證了使用不同的類加載器最終得到的都是同樣一個 Object
對象。
OSGi 動態模型系統
OSGi(Open Service Gateway Initiative)是面向 Java 的動態模型系統,是 Java 動態化模塊化系統的一系列規範
動態改造構造
OSGi 服務平臺提供在多種網絡設備上無需重啓的動態改變構造的功能。
爲了最小化耦合度和促使這些耦合度可管理, OSGi 技術提供一種面向服務的架構,它能使這些組件動態地發現對方。
模塊化編程與熱插拔
OSGi 旨在爲實現 Java 程序的模塊化編程提供基礎條件,基於 OSGi 的程序可以實現模塊級的熱插拔功能,當程序升級更新時,可以只停用、重新安裝然後啓動程序的其中一部分,這對企業級程序開發來說是非常具有誘惑力的特性。
OSGi 描繪了一個很美好的模塊化開發目標,而且定義了實現這個目標所需要的服務與架構,同時也有成熟的框架進行實現支持。但並非所有的應用都適合採用 OSGi 作爲基礎架構,它在提供強大功能同時,也引入了額外的複雜度,因爲它不遵守類加載的雙親委託模型。