加載
- 加載是類加載過程當中的一個階段,這個階段會在內存中生成一個表明這個類的java.lang.Class對象,做爲方法區這個類的各類數據的入口。
- 注意這裏不必定非得要從一個Class文件獲取,這裏既能夠從ZIP包中讀取(好比從jar包和war包中讀取),也能夠在運行時計算生成(動態代理),也能夠由其它文件生成(好比將JSP文件轉換成對應的Class類)。
驗證
- 這一階段的主要目的是爲了確保Class文件的字節流中包含的信息是否符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。
準備
- 準備階段是正式爲類變量分配內存並設置類變量的初始值階段
- 即在方法區中分配這些變量所使用的內存空間。
- 注意這裏所說的初始值概念,好比一個類變量定義爲:
public
static
int
v =
8080
;
- 實際上變量v在準備階段事後的初始值爲0而不是8080
- 將v賦值爲8080的putstatic指令是程序被編譯後,存放於類構造器<client>方法之中
- 可是注意若是聲明爲:
public
static
final
int
v =
8080
;
- 在編譯階段會爲v生成ConstantValue屬性,在準備階段虛擬機會根據ConstantValue屬性將v賦值爲8080。
解析
- 解析階段是指虛擬機將常量池中的符號引用替換爲直接引用的過程。
- 符號引用就是class文件中的:
- CONSTANT_Class_info
- CONSTANT_Field_info
- CONSTANT_Method_info
- 下面咱們解釋一下符號引用和直接引用的概念:
- 符號引用與虛擬機實現的佈局無關,引用的目標並不必定要已經加載到內存中。
- 各類虛擬機實現的內存佈局能夠各不相同,可是它們能接受的符號引用必須是一致的,
- 由於符號引用的字面量形式明肯定義在Java虛擬機規範的Class文件格式中。
- 直接引用能夠是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄。
- 若是有了直接引用,那引用的目標一定已經在內存中存在。
初始化
- 初始化階段是類加載最後一個階段,前面的類加載階段以後,除了在加載階段能夠自定義類加載器之外,其它操做都由JVM主導。
- 到了初始階段,纔開始真正執行類中定義的Java程序代碼。
- 初始化階段是執行類構造器<client>方法的過程。
- <client>方法是由編譯器自動收集類中的類變量的賦值操做和靜態語句塊中的語句合併而成的。
- 虛擬機會保證<client>方法執行以前,父類的<client>方法已經執行完畢。
- p.s: 若是一個類中沒有對靜態變量賦值也沒有靜態語句塊,那麼編譯器能夠不爲這個類生成<client>()方法。
注意如下幾種狀況不會執行類初始化(!!!):java
- 經過子類引用父類的靜態字段,只會觸發父類的初始化,而不會觸發子類的初始化。
- 定義對象數組,不會觸發該類的初始化。
- 常量在編譯期間會存入調用類的常量池中,本質上並無直接引用定義常量的類,不會觸發定義常量所在的類。
- 經過類名獲取Class對象,不會觸發類的初始化。
- 經過Class.forName加載指定類時,若是指定參數initialize爲false時,也不會觸發類初始化,其實這個參數是告訴虛擬機,是否要對類進行初始化。
- 經過ClassLoader默認的loadClass方法,也不會觸發初始化動做。
類加載器
3種類加載器:數組
- 啓動類加載器(Bootstrap ClassLoader):
- 負責加載 JAVA_HOME\lib 目錄中的,
- 或經過-Xbootclasspath參數指定路徑中的,
- 且被虛擬機承認(按文件名識別,如rt.jar)的類。
- 擴展類加載器(Extension ClassLoader):
- 負責加載 JAVA_HOME\lib\ext 目錄中的,
- 或經過java.ext.dirs系統變量指定路徑中的類庫。
- 應用程序類加載器(Application ClassLoader):
JVM經過雙親委派模型進行類的加載,固然咱們也能夠經過繼承java.lang.ClassLoader實現自定義的類加載器。安全
- 當一個類加載器收到類加載任務,會先交給其父類加載器去完成,所以最終加載任務都會傳遞到頂層的啓動類加載器,
- 只有當父類加載器沒法完成加載任務時,纔會嘗試執行加載任務。
採用雙親委派的一個好處是佈局
- 好比加載位於rt.jar包中的類java.lang.Object,無論是哪一個加載器加載這個類,最終都是委託給頂層的啓動類加載器進行加載,
- 這樣就保證了使用不一樣的類加載器最終獲得的都是一樣一個Object對象。
jdk中的ClassLoader的源碼實現:spa
- 首先經過Class c = findLoadedClass(name);判斷一個類是否已經被加載過。
- 若是沒有被加載過執行if (c == null)中的程序,遵循雙親委派的模型,首先會經過遞歸從父加載器開始找,直到父類加載器是Bootstrap ClassLoader爲止。
- 最後根據resolve的值,判斷這個class是否須要解析。