Java 類加載機制
- 1、類加載過程詳解
-
- 1.1、類加載過程詳解圖
- 1.2、類生命周期
- 1.3、類生命周期各階段詳解說明
- 2、類加載器
- 3、雙親委派模型
1、類加載過程詳解
1.1、類加載過程詳解圖
1.2、類生命周期
- 一個類從被加載到虛拟機記憶體中開始,到解除安裝出記憶體為止,他的生命周期将會經曆以下七個階段。
需要說明的是:
-
-->加載
-->驗證
-->準備
-->初始化
,這五個階段的順序是确定的,類的加載過程必須按該順序開始執行(隻要求按順序開始,不要求前一個結束了,下一個再開始)。這五個階段通常是互相交叉地混合進行,會在一個階段執行的過程中調用、激活另一個階段。解除安裝
-
階段在某些情況下可以在初始化階段之後開始。解析
1.3、類生命周期各階段詳解說明
加載:
|_
通過一個類的全限定名,在硬碟中查找并通過IO 加載類的二進制資料(class檔案)
|_
轉為方法區資料結構,并存放到方法區。方法區:類的類資訊。
|_
在 Java 堆中産生 java.lang.Class 對象。堆:Class 檔案對應的類執行個體。
驗證: 作用為驗證 class 檔案是否符合規範。
|_
檔案格式的驗證:
|_ 是否以 0xCAFEBABE 開頭。
|_ 版本号是否合理。
|_
中繼資料驗證:
|_ 是否有父類。
|_ 是否繼承了 final 類(final 類不能被繼承,如果繼承了就有問題)
|_ 非抽象類實作了所有抽象方法。
|_
位元組碼驗證:
|_ 運作檢查。
|_
棧資料類型和操作碼操作參數吻合(例如棧空間隻有2位元組,但其實卻需要大于2位元組,此時就認為這個位元組碼是有問題的)
|_ 跳轉指令指向合理的位置。
|_
符合引用驗證:
|_ 常量池中描述類是否存在。
|_ 通路的方法或字段是否存在且有足夠的權限。
|_
說明:可使用 -Xverify:none 關閉驗證。
準備:
|_
作用:為類的靜态變量進行初始化,并為類的靜态變量配置設定記憶體空間,并賦予初始值。
|_ final static 修飾的變量:直接指派為使用者定義的值,例如:
|_ private final static int value = 123,直接指派 123.
|_ private static int value = 123,該階段的值依然是 0.
解析:
是将符号引用轉換為直接引用.
初始化:
JVM 對類進行初始化,對靜态變量賦予正确值.
|_
執行<clinit>方法,clinit 方法由編譯器自動收集類裡面的所有靜态變量的指派動作及靜态語句塊合并而成,也叫類構造器方法。
|_
初始化的順序和源檔案中的順序一緻。
|_
子類的<clinit>被調用前,會先調用父類的<clinit>方法。
|_
JVM 會保證 clinit 方法的線程安全性。
|_
初始化時,如果執行個體化一個新對象,會調用<init>方法對執行個體變量進行初始化,并執行對應的構造方法内的代碼。
|_
構造塊在構造方法之前執行。
2、類加載器
類加載器隻用于實作類的加載動作,對于任意一個類,都必須由加載它的類加載器和這個類本身一起共同确定其在 Java 虛拟機中的唯一性。每一個類加載器,都擁有一個獨立的類名稱空間。
- 類加載器由進階别到低級别依次為:
-
- |_ (負責加載 JDK/JRE/LIB目錄下,或者被-Xbootclasspath參數所指定的路徑中存放的,能被識别的類庫,到虛拟機的記憶體中)
- |_ (負責加載 JDK/JRE/LIB/EXT 目錄下,或者被java.ext.dirs系統變量所指定的路徑中所有的類庫)
- |_ 負責加載使用者類路徑 (ClassPath)上所有的類庫,開發者可以直接在代碼中使用這個類加載器
- |_ 流、網絡、資料庫
BootStrapClassLoader(啟動類加載器—C語言寫的)
ExtClassLoader(擴充類加載器)
AppClassLoader(應用程式類加載器)
使用者自定義類加載器
3、雙親委派模型
如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把請求委托給父加載器去完成,依次向上。是以,所有的類加載請求最終都應該被傳遞到頂層的啟動類加載器中,隻有當父加載器在它的搜尋範圍中沒有找到所需的類時,即無法完成該加載,子加載器才會嘗試自己去加載該類。
- 雙親委派模型的實作源碼如下:
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 首先,檢查請求的類是否已經被加載過了
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// 如果父類加載器抛出ClassNotFoundException
// 說明父類加載器無法完成加載請求
}
if (c == null) {
// 在父類加載器無法加載時
// 再調用本身的findClass方法來進行類加載
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
從源碼可以看出:
先檢查請求加載的類型是否已經被加載過,若沒有則調用父加載器的 loadClass()方法,若父加載器為空則預設使用啟動類加載器作為父加載器。假如父類加載器加載失敗, 抛出ClassNotFoundException異常的話,才調用自己的findClass()方法嘗試進行加載。
.