十四:類加載的過程

加載->驗證->準備->解析->初始化

 

1.加載

比如運行時計算生成,這種場景使用得最多的就是動態代理技術,在java.lang.reflect.Proxy中,就是用了ProxyGenerator.generateProxyClass來爲特定接口生成形式爲“*$Proxy”的代理類的二進制字節流

 

加載階段與連接階段的部分內容(如一部分字節碼文件格式驗證動作)是交叉進行的,加載階段尚未完成,連接階段可能已經開始,但這些夾在加載階段之中進行的動作,仍然屬於連接階段的內容,這兩個階段的開始時間仍然保持着固定的先後順序

 

2.驗證

這是連接階段的第一步,目的是確保Class文件的字節流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全,避免因爲載入了有害的字節流而導致系統崩潰

驗證階段大致完成下面4個檢驗動作:

2.1 文件格式驗證

檢驗字節流是否是符合Class文件規範,比如是否以魔數0xCAFEBABE開頭、版本號是否在當前虛擬機處理範圍之內、常量池中是否有不被支持的常量類型等等

2.2 元數據校驗

主要是對字節碼描述的信息進行語義分析,比如這個類是否有父類(除了Object類沒有外,其他都必須有)、如果這個類不是抽象類,是否實現了其父類或者接口之中要求實現的所有方法

2.3 字節碼驗證

主要是通過數據流和控制流進行分析,比如確保跳轉指令不會跳轉到方法體以外的字節碼指令上面、確保方法體中的類型轉換是有效的

但是通過字節碼驗證也不是代表它絕對沒有問題,因爲不能夠準確檢查出它是否能夠在有效時間內結束運行。

2.4 符號引用驗證

這個階段的校驗是發生在虛擬機將符號引用轉化爲直接引用的時候發生的,具體來說就是連接的解析階段。比如能否在符號引用中通過字符串描述的全限定名找到對應的類

 

3.準備

準備階段是正式爲類變量分配內存並設置類變量初始值的階段,這些變量所使用的內存都將在堆內存(也可以說是方法區)中進行分配

需要注意的是,首先,這時候進行內存分配的僅包括類變量(被static修飾的變量)而不包括實例變量,實例變量將會在對象實例化時隨着對象一起分配在Java堆中。其次,這裏所說的初始值“通常情況”下是數據類型的零值,零值如圖:

“通常情況”下初始值是零值,那相對的會有一些“特殊情況”:如果類字段的字段屬性表中存在ConstantValue屬性,那在準備階段變量value就會被初始化爲ConstantValue屬性所指定的值,假設上面類變量value的定義變爲

public static final int value = 123

編譯時Javac將會爲value生成ConstantValue屬性,在準備階段虛擬機就會根據Constantvalue的設置將value賦值爲123

 

4.解析

解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程,有類或接口解析、字段解析、類方法解析、接口方法解析

 

5.初始化

初始化階段是執行類構造器<clinit>()方法的過程。

下面是<clinit>()的產生過程:

結果爲2

注:同一個類加載器下,一個Class(類型)只會初始化一次

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章