JVM 類加載機制分為五個部分:加載待榔,驗證,準備流济,解析锐锣,初始化,下面我們就分別來看一下這五個過程绳瘟。
1.1 加載
加載是類加載過程中的一個階段雕憔,這個階段會在內存中生成一個代表這個類的java.lang.Class 對象,作為方法區(qū)這個類的各種數(shù)據(jù)的入口糖声。注意這里不一定非得要從一個Class 文件獲取斤彼,這里既可以從ZIP 包中讀取(比如從jar 包和war 包中讀日盒骸)畅卓,也可以在運行時計算生成(動態(tài)代理),也可以由其它文件生成(比如將JSP 文件轉換成對應的Class 類)蟋恬。
1.2 驗證
這一階段的主要目的是為了確保Class 文件的字節(jié)流中包含的信息是否符合當前虛擬機的要求,并且不會危害虛擬機自身的安全趁冈。
1.3. 準備
準備階段是正式為類變量分配內存并設置類變量的初始值階段歼争,即在方法區(qū)中分配這些變量所使用的內存空間。注意這里所說的初始值概念渗勘,比如一個類變量定義為:
public static int v = 8080;
實際上變量v 在準備階段過后的初始值為0 而不是8080沐绒,將v 賦值為8080 的put static 指令是程序被編譯后,存放于類構造器<client>方法之中旺坠。但是注意如果聲明為:
public static final int v = 8080;
在編譯階段會為v 生成ConstantValue 屬性乔遮,在準備階段虛擬機會根據(jù)ConstantValue 屬性將v賦值為8080。
1.4 解析
解析階段是指虛擬機將常量池中的符號引用替換為直接引用的過程取刃。符號引用就是class 文件中的:
1. CONSTANT_Class_info
2. CONSTANT_Field_info
3. CONSTANT_Method_info
等類型的常量蹋肮。
1.5 符號引用
?? 符號引用與虛擬機實現(xiàn)的布局無關,引用的目標并不一定要已經(jīng)加載到內存中璧疗。各種虛擬
機實現(xiàn)的內存布局可以各不相同坯辩,但是它們能接受的符號引用必須是一致的,因為符號引用的字面量形式明確定義在Java 虛擬機規(guī)范的Class 文件格式中崩侠。
1.6. 直接引用
?? 直接引用可以是指向目標的指針漆魔,相對偏移量或是一個能間接定位到目標的句柄。如果有了直接引用,那引用的目標必定已經(jīng)在內存中存在改抡。
1.7. 初始化
初始化階段是類加載最后一個階段矢炼,前面的類加載階段之后,除了在加載階段可以自定義類加載
器以外阿纤,其它操作都由JVM 主導句灌。到了初始階段,才開始真正執(zhí)行類中定義的Java 程序代碼阵赠。
1.8. 類構造器<client>
初始化階段是執(zhí)行類構造器<client>方法的過程涯塔。<client>方法是由編譯器自動收集類中的類變
量的賦值操作和靜態(tài)語句塊中的語句合并而成的。虛擬機會保證子<client>方法執(zhí)行之前清蚀,父類的<client>方法已經(jīng)執(zhí)行完畢匕荸,如果一個類中沒有對靜態(tài)變量賦值也沒有靜態(tài)語句塊,那么編譯器可以不為這個類生成<client>()方法枷邪。
注意以下幾種情況不會執(zhí)行類初始化:
1. 通過子類引用父類的靜態(tài)字段榛搔,只會觸發(fā)父類的初始化,而不會觸發(fā)子類的初始化东揣。
2. 定義對象數(shù)組践惑,不會觸發(fā)該類的初始化。
3. 常量在編譯期間會存入調用類的常量池中嘶卧,本質上并沒有直接引用定義常量的類尔觉,不會觸發(fā)定義常量所在的類。
4. 通過類名獲取Class 對象芥吟,不會觸發(fā)類的初始化侦铜。
5. 通過Class.forName 加載指定類時,如果指定參數(shù)initialize 為false 時钟鸵,也不會觸發(fā)類初始化钉稍,其實這個參數(shù)是告訴虛擬機,是否要對類進行初始化棺耍。
6. 通過ClassLoader 默認的loadClass 方法贡未,也不會觸發(fā)初始化動作。