深入探究JVM之類載入與雙親委派機制
阿新 • • 發佈:2020-08-01
@[toc]
# 前言
前面學習了虛擬機器的記憶體結構、物件的分配和建立,但物件所對應的類是怎麼載入到虛擬機器中來的呢?載入過程中需要做些什麼?什麼是雙親委派機制以及為什麼要打破雙親委派機制?
# 類的生命週期
![在這裡插入圖片描述](https://img-blog.csdnimg.cn/2020073120514156.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2w2MTA4MDAz,size_16,color_FFFFFF,t_70)
類的生命週期包含了如上的7個階段,其中**驗證**、**準備**、**解析**統稱為**連線 **,類的載入主要是前五個階段,每個階段基本上保持如上順序**開始**(僅僅是開始,實際上執行是交叉混合的),只有**解析**階段不一定,在**初始化後**也有可能才開始執行解析,這是為了支援動態語言。
## 載入
載入就是將位元組碼的二進位制流轉化為**方法區**的執行時資料結構,並生成類所物件的Class物件,位元組碼二進位制流可以是我們編譯後的class檔案,也可以從網路中獲取,或者執行時動態生成(動態代理)等等。
那什麼時候會觸發類載入呢?這個在虛擬機器規範中沒有明確定義,只是規定了何時需要執行初始化(稍後詳細分析)。
## 驗證
這個階段很好理解,就是進行必要的校驗,確保載入到記憶體中的位元組碼是符合要求的,主要包含以下四個校驗步驟(瞭解即可):
- 檔案格式校驗:這個階段要校驗的東西非常多,主要的有下面這些(實際上遠遠不止)
- 是否以魔數0xCAFEBABE開頭。
- 主、次版本號是否在當前Java虛擬機器接受範圍之內。
- 常量池的常量中是否有不被支援的常量型別(檢查常量tag標誌)。
- 指向常量的各種索引值中是否有指向不存在的常量或不符合型別的常量。
- CONSTANT_Utf8_info型的常量中是否有不符合UTF-8編碼的資料。
- Class檔案中各個部分及檔案本身是否有被刪除的或附加的其他資訊。
- 。。。。。。
- 元資料校驗:對位元組碼描述資訊進行語義分析。
- 這個類是否有父類(除了java.lang.Object之外,所有的類都應當有父類)。
- 這個類的父類是否繼承了不允許被繼承的類(被final修飾的類)。
- 如果這個類不是抽象類,是否實現了其父類或介面之中要求實現的所有方法。
- 類中的欄位、方法是否與父類產生矛盾(例如覆蓋了父類的final欄位,或者出現不符合規則的方法過載,例如方法引數都一致,但返回值型別卻不同等)。
- 。。。。。。
- 位元組碼校驗:確保程式沒有語法和邏輯錯誤,這是整個驗證階段最複雜的一個步驟。
- 保證任意時刻運算元棧的資料型別與指令程式碼序列都能配合工作,例如不會出現類似於“在操作棧放置了一個 int 型別的資料,使用時卻按 long 型別來載入入本地變量表中”這樣的情況。
- 保證任何跳轉指令都不會跳轉到方法體以外的位元組碼指令上。
- 保證方法體中的型別轉換總是有效的,例如可以把-個子類物件賦值給父類資料型別,這是安全的,但是把父類物件賦值給子類資料型別,甚至把物件賦值給與它毫無繼承關係、完全不相干的一個數據型別,則是危險和不合法的。
- 。。。。。。
- 符號引用驗證:這個階段發生在符號引用轉為直接引用的時候,即實際上是在**解析**階段中進行的。
- 符號引用中通過字串描述的全限定名是否能找到對應的類。
- 在指定類中是否存在符合方法的欄位描述符及簡單名稱所描述的方法和欄位。
- 符號引用中的類、欄位、方法的可訪問性( private、 protected. pu