JVM之類載入器子系統
阿新 • • 發佈:2021-03-02
# 類載入器子系統
![](https://img2020.cnblogs.com/blog/1057749/202103/1057749-20210302203712545-606045767.png)
## 作用
1. 負責從檔案系統或網路系統中載入class檔案,class檔案在開頭有特殊的標記(魔術開頭CA FE BA BE)
2. ClassLoader只負責載入class檔案,至於能否執行,由執行引擎決定
3. 載入的類的資訊存放於一塊稱為方法區的記憶體空間。除了類的資訊外,方法區中還會存放執行時常量池資訊,可能還包括字串字面量和數字常量(這部分常量資訊是Class檔案中常量池部分的記憶體對映)
## 類載入過程
![](https://img2020.cnblogs.com/blog/1057749/202103/1057749-20210302203917937-505136071.png)
### 載入
- 通過一個類的全限定類名獲取定義此類的二進位制位元組流
- 將這個位元組碼所代表的靜態儲存結構轉化為方法區的執行時資料
- 在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的各種資料訪問入口
![](https://img2020.cnblogs.com/blog/1057749/202103/1057749-20210302203932893-465138005.png)
### 連結
#### 驗證
- 目的在於確定class檔案的位元組流中包含資訊符合當前虛擬機器要求,保證被載入類的正確性,不會危害虛擬機器自身安全
- 主要包括四種驗證,檔案格式驗證,元資料驗證,位元組碼驗證,符號引用驗證
- 檔案格式驗證主要驗證魔術開頭(CAFE BABE),常量池中常量的型別、版本號等
- 元資料驗證:這個類是否有父類,Object類除外,是否繼承final類
#### 準備
為類變數分配記憶體,並設定類變數初始值,基本型別為預設值,引用型別為null
這裡不包含final修飾的static變數,因為final修飾的變數在編譯的時候就會分配好預設值,準備階段會顯式初始化
準備階段不會為例項化變數初始化,類變數分配在方法區中,實力變數分配在Java堆中
#### 解析
將常量池內的符號引用轉換為直接引用的過程
事實上,解析操作往往會伴隨著JVM在執行完初始化之後再執行
符號引用就是一組符號來描述所引用的目標。符號引用的字面量形式明確定義在《java虛擬機器規範》的class檔案格式中。直接引用就是直接指向目標的指標、相對偏移量或一個間接定位到目標的控制代碼
解析動作主要針對類或介面、欄位、類方法、介面方法、方法型別等。對應常量池中的CONSTANT Class info、CONSTANT Fieldref info、CONSTANT Methodref info等
### 初始化
> #### 類的初始化時機
>
> 1、建立類的例項
> 2、訪問某個類或者介面的靜態變數,或者對該靜態變數複製
> 3、呼叫類的靜態方法
> 4、反射 Class.forName("com.chaoba.Test")
> 5、初始化一個類的子類
> 6、java虛擬機器啟動時被標記為啟動的類
> 7、JDK7開始提供的動態語言支援:java.lang.invoke.MethodHandle例項的解析結果REF_getStatic、REF putStatic、REF_invokeStatic控制代碼對應的類沒有初始化,則初始化
> 除了以上七種情況,其他使用Java類的方式都被看作是對類的被動使用,都不會導致類的初始化,即不會執行初始化階段(不會呼叫 clinit() 方法和 init() 方法)
- 初始化階段就是執行類構造器()方法的過程
- 此方法不需要被定義,是javac編輯器自動收集類中的所有類變數的賦值動作和靜態程式碼塊中的語句合併而來
- 構造器方法中指令按語句在原始檔中出現的順序執行
- ()不同於類的構造器(關聯:構造器是虛擬機器視角下的())
- 若該類具有父類,則應該先執行父類的構造方法
- 虛擬機器必須保證一個類的()在多執行緒環境下被同步加鎖
## 類載入器的分類
### 引導類載入器(Bootstrap ClassLoader)
- 載入Java的核心庫(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路徑下的內容),用於提供JVM自身需要的類
- 沒有繼承自ClassLoader類,沒有父載入器
- 載入擴充套件類和應用程式類載入器,並作為他們的父類載入器
- 出於安全考慮,Bootstrap啟動類載入器只加載包名為java、javax、sun等開頭的類
### 擴充套件載入器
- 父類為引導類載入器
- Java語言編寫,由sun.misc.Launcher$ExtClassLoader實現
- 從java.ext.dirs系統屬性所指定的目錄中載入類庫,或從JDK的安裝目錄的jre/lib/ext子目錄(擴充套件目錄)下載入類庫。如果使用者建立的JAR放在此目錄下,也會自動由擴充套件類載入器載入
### 應用載入器(系統載入器)
- 系統類載入器是全域性唯一的
- Java語言編寫,由sun.misc.LaunchersAppClassLoader實現
- 負責載入環境變數classpath或系統屬性java.class.path指定路徑下的類庫
- 該類載入是程式中預設的類載入器,一般來說,Java應用的類都是由它來完成載入
- 通過classLoader.getSystemclassLoader()方法可以獲取到該類載入器
### 自定義類載入器(User-Defined ClassLoader)
- 父類為應用載入器
- 將所有派生於抽象類ClassLoader的類載入器都劃分為自定義類載入器
## 雙親委派機制
![](https://img2020.cnblogs.com/blog/1057749/202103/1057749-20210302204017514-1716831580.png)
Java虛擬機器對class檔案採用的是按需載入的方式,也就是說當需要使用該類時才會將它的class檔案載入到記憶體生成class物件。而且載入某個類的class檔案時,Java虛擬機器採用的是雙親委派模式,即把請求交由父類處理,它是一種任務委派模式
### 優勢
- 避免類的重複載入
- 保護程式安全,防止核心API被隨意篡改
## 沙箱安全機制
![](https://img2020.cnblogs.com/blog/1057749/202103/1057749-20210302204029232-875280133.png)
## 其他
![](https://img2020.cnblogs.com/blog/1057749/202103/1057749-20210302204036004-16617086