JVM之類載入子系統
阿新 • • 發佈:2020-12-31
JVM之類載入器
所在位置
詳細圖
類載入子系統作用
- 類載入子系統負責從檔案系統或者網路中載入
class
檔案,class
檔案在檔案開頭有特定的檔案標識、 ClassLoader
只負責class
檔案的載入,至於它是否可以執行,則由Execution Egine(執行引擎)
來決定。- 載入的類資訊存放於一塊稱為
方法區
的記憶體空間。除了類資訊外,方法區還會存放執行時常量池資訊,可能還包含字串字面量和數字常量(這部分常量資訊是class檔案中常量池部分的記憶體對映)
ClassLoad角色
class file
存在於本地硬碟中,可以理解為設計師畫在紙上的模板,而最終這個模板在執行的時候是要載入到JVM當中,根據這個檔案例項化出n個一模一樣的例項。class file
載入到JVM中,被稱為NDA元資料模板,放在方法區。- 在·.class·檔案 -> JVM -> 最終資料模板,這個過程就是
ClassLoader
要做的事情。
類的載入過程
載入(Loading)
- 1、通過一個類的全限定名獲取此類的二進位制位元組流
- 2、將這個位元組流所代表的靜態儲存結構轉化為方法區執行時資料結構
- 3、在記憶體中生成一個代表這個類的
java.lang.Class
物件,作為方法區這個類的各種資料訪問入口
連結(Linking)
驗證(Verify)
- 目的在於確保
class
檔案中位元組流中包含資訊符合當前虛擬機器要求,保證被載入類的正確性,不會危害虛擬機器自身安全。 - 主要包括4中驗證:檔案格式驗證、元資料驗證、位元組碼驗證、符號引用驗證
準備(Prepare)
- 為類變數分配記憶體並且設定該變數的預設初始值,即零值。
- 這裡不包含用
final
修飾的static
,因為final
在編譯的時候就已經分配了,準備階段會顯式初始化。 - 這裡不會為例項變數分類初始化,類變數會分配在方法區中,而例項變數是會隨著物件一起分配到Java堆中。
解析(Resolve)
- 將常量池內的符號引用轉換為直接引用的過程
- 事實上,解析操作往往會伴隨著JVM在執行完初始化後在執行。
- 符號引用就是一組符號來描述引用的目標。符號引用的字面量形式明確定義在《Java虛擬機器規範》的
Class
- 解析動作主要針對類或介面。欄位、類方法、介面方法、方法型別等。對常量池中的
CONSTANT_Class_info
、CONSTANT_Fieldref_info
、CONSTANT_Methodef_info等
初始化(Initialization)
- 初始化階段就是指向類構造器方法
()的過程 - 此方法不需要定義,是javac編譯器自動收集類中的所有類變數的賦值動作和靜態程式碼塊中的語句合併而來的。
- 構造器方法中指令按照語句在原始檔中出現的順序執行。
()不同於類構造器(構造器是虛擬機器視角下的 ()) - 若該類具有父類,JVM會保證子類的
()執行前,父類的 (已經執行完畢) - 虛擬機器必須保證一個類的
()方法在多執行緒下被同步加鎖。
類載入器分類
啟動類載入器(引導類載入器 Bootstrap ClassLoader)
- 這個類載入器使用
c/c++
語言實現,巢狀在JVM內部。 - 它用來載入Java的核心庫(JAVA_HOME/jar/lib/rt.jar、resources.jar、sun.boot.class.path路徑下的內容),用於提供JVM自身需要的類
- 並不繼承自
java.lang.ClassLoader
,沒有父載入器。 - 夾雜擴充套件類和應用程式類記載其,並指定為它們的父類載入器。
- 出於安全考慮,Bootstrap啟動類載入器只加載包名為
java
、javax
、sun
等開頭的類
擴充套件類載入器(Extension ClassLoader)
- Java語言編寫,由
sun.misc.Launcher$ExtClassLoader
實現。 - 派生於
ClassLoader
類 - 從
java.ext.dirs
系統屬性所指定的目錄中載入類庫,或從JDK的安裝包目錄jar/lib/ext
子目錄下下載類庫。如果用建立的JAR放在此目錄下,也會自動有擴充套件類載入器載入。
應用程式載入器(系統類載入器 AppClassLoader)
- java 語言編寫,由
sun.misc.Launcher$AppClassLoader
實現 - 派生與
ClassLoader
類 - 它負責載入環境變數
classpath
或者系統屬性java.class.path
指定路徑下的類庫 - 該類載入器是程式中預設的載入器,一般來說,Java應用的類都是由它完成載入
- 通過
ClassLoader#getSystemClassLoader()
方法可以獲取該類載入器。
檢視類載入器可載入的路徑
package top.mgy.classloader;
import java.net.URL;
import static sun.misc.Launcher.getBootstrapClassPath;
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println("*****啟動類載入器******");
//獲取BootstrapClassLoader能載入的類庫路徑
URL[] urLs = getBootstrapClassPath().getURLs();
for (URL url : urLs) {
System.out.println(url.toExternalForm());
}
System.out.println("*******擴充套件類載入器********");
String extDirs = System.getProperty("java.ext.dirs");
for (String path:extDirs.split(";")){
System.out.println(path);
}
}
}
使用者自定義類載入器
在Java的日常應用程式開發中,類載入器幾乎由上述3中載入器相互配合執行的,在必要時,我們可以自定義類載入器,來定製類的載入方式。
為什麼要自定義類載入器?
- 隔離載入類
- 修改類的載入方式
- 擴充套件載入源
- 防止原始碼洩露
如何自定義類載入器?
獲取ClassLoader
幾種途徑
雙親委派機制
Java虛擬機器對
Class
檔案採用的是按需載入的方式,也就是說當需要使用該類時才會將它的class檔案載入到記憶體生成class
物件。而且載入某個類的class
檔案時,Java虛擬機器採用的是雙親委派模式,即把請求交由父類處理,它是一種任務委派模式
工作原理
- 1、如果一個類載入器收到了類載入請求,它並不會自己先去載入,而是把這個請求委託給父類的載入器去執行
- 2、如果父類載入還存在父類載入器,則進一步向上委託,依次遞迴,請求最終價格到達頂層的啟動類載入器。
- 3、如果父類載入器可以完成載入任務,就成功返回,若父類載入器無法完成載入任務,子載入器才會嘗試自己去載入,這就是雙親委派模式。
雙親委派機制的優勢
-
避免類被重複載入
-
保護程式安全,防止核心API被隨意篡改
自定義類
java.lang.String
自定義類
java.lang.Haha
沙箱安全機制
自定義String
類,在載入自定義String
類的時候會先使用引導類載入器載入,而引導類載入器在載入的過程中會先載入jdk自帶的檔案,報錯資訊說沒有main
方法就是因為載入的是rt.jar
包中String
類。這樣可以保證對java
核心原始碼的保護,這就是沙箱安全機制。