虛擬機類加載總結
時間:20180223
一垄懂、學習總結
知識儲備
什么是靜態(tài)方法?
什么是靜態(tài)類痛垛?
什么是常量池草慧?
什么是靜態(tài)變量?
二匙头、類加載機制流程
1.什么是類加載機制漫谷?
JVM把class文件加載到內(nèi)存里面,并對數(shù)據(jù)進行校驗蹂析、準備舔示、解析和初始化,最總形成可以被JVM直接使用的java類型的過程电抚。
2.類的加載流程圖
3.類加載的過程--加載
”加載“是”類加載(Class Loading )“過程的一個階段惕稻,切記不要混淆。
在加載階段蝙叛,虛擬機需要完成以下三個階段俺祠。
1)將class文件記載到內(nèi)存中。(通過類的全限定名來獲取定義此類的二進制字節(jié)流)。
2)將上述字節(jié)流代表的靜態(tài)數(shù)據(jù)結構(數(shù)據(jù)存在于class文件的結構)轉化成方法區(qū)中運行時的數(shù)據(jù)結構(數(shù)據(jù)存在于JVM時的數(shù)據(jù)結構)蜘渣。
3)在內(nèi)存中(堆)生成一個代表這個類的class對象(java.lang.class)淌铐,作為數(shù)據(jù)(方法區(qū))訪問入口。
4.鏈接
鏈接就是將java類的二進制代碼合并到java的運行狀態(tài)中的過程蔫缸。
- 驗證:確保加載的類符合JVM規(guī)范與安全(確保Class文件的字節(jié)流中包含的信息符合JVM規(guī)范與安全)
- 準備:為類變量(static變量)分配內(nèi)存(在方法區(qū)中分配空間)腿准,并設置類變量的初始值。注意 -- 這里的類變量僅值被static修飾的變量拾碌,而不包括實例變量释涛;實例變量將會在對象的實例化時隨著對象一起分配在java堆中。例如public static int a = 123; 在此階段a被初始化為0倦沧,其它數(shù)據(jù)類型參考成員變量聲明唇撬。
//TO DO待加入Test代碼
- 解析:虛擬機將常量池的符號引用轉變成直接引用。例如"aaa"為常量池的一個值展融,直接把"aaa"替換成存在于內(nèi)存中的地址窖认。
符號引用:符號引用以一組符號(全限定名)來描述所引用的目標,符號可以是任何形式的字面量告希,只要使用時能無歧義地定位到目標既可以扑浸。符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關,引用的目標并不一定已經(jīng)加載到內(nèi)存中燕偶。
直接引用:直接引用可以使直接指向目標的指針喝噪、相對偏移量或是一個能間接定位到目標的句柄。直接應用是與虛擬機實現(xiàn)的內(nèi)存布局相關指么。如果有了直接引用酝惧,那么引用的目標必定已經(jīng)在內(nèi)存中存在。
- 初始化:初始化是類加載過程的最后一步伯诬。初始化階段是執(zhí)行類構造器<clinit>()方法的過程晚唇。
在了加載過程中,除了在加載階段用戶應用程序可以通過自定義類加載器參與外盗似,其余動作完全由虛擬機主導和控制哩陕。到了初始化階段,才真正開始執(zhí)行類中自定義的java程序代碼(或者說是字節(jié)碼)赫舒。
<clinit>()方法是由編譯器自動搜集類中的所有類變量的賦值動作(準備階段的 static a 正式被賦值為a)和靜態(tài)變量與靜態(tài)語句塊static{}合并產(chǎn)生的悍及。
注意 編譯器手機的順序是由語句在源文件中出現(xiàn)的順序決定的,靜態(tài)語句塊只能訪問到定義在靜態(tài)語句塊之前的變量接癌;定義在之后的變量心赶,在前面的靜態(tài)語句塊可以賦值,但是不能訪問扔涧。
例如下列情況园担,會報錯届谈,但報錯的地方不是賦值語句,而是訪問語句弯汰。
三艰山、類加載與初始化時機
1.類加載時機
當應用程序啟動時候,所有的類會被一次性加載么咏闪?當然不能曙搬,因為一次性加載所有的類,內(nèi)存資源有限鸽嫂,可能會影響應用程序的正常運行纵装。那類什么時候才會被加載呢?例如在 A a = new A()据某,一個類真正被加載的時機是在創(chuàng)建對象的時候橡娄,才會去執(zhí)行以上過程,加載類癣籽。當我們測試的時候挽唉,最新加載擁有main方法的主線程所在的類。
2.類初始化時機
主動引用(發(fā)生類初始化過程)
1.new 一個對象(使用new關鍵字實例化對象)
2.調用類的靜態(tài)成員(除了final常量)和靜態(tài)方法筷狼。
3.通過反射對類進行調用
4.虛擬機啟動瓶籽,main方法所在類被提前初始化。
5.初始化一個類埂材,如果父類沒有初始化塑顺,則先初始化父類。
被動引用(不會發(fā)生類的初始化)
1.當訪問一個靜態(tài)變量時俏险,只有真正生命這個變量的類才會初始化严拒。(例如:子類調用父類的靜態(tài)變量,只有父類初始化寡喝,子類不初始化)糙俗。
2.通過數(shù)組定義類的引用勒奇。不會觸發(fā)此類的初始化预鬓。
3.final變量不會觸發(fā)此類的初始化,因為在編譯階段就存儲在常量池中赊颠。
四格二、圖解分析類加載
package com.amp;
public class ClassLoaderProduce {
static int d = 3;
static {
System.out.println("我是ClassLoaderProduce類");
}
public static void main(String [] args) {
int b = 0;
String c = "hello";
SimpleClass simpleClass = new SimpleClass();
simpleClass.run();
}
}
package com.amp;
public class SimpleClass {
static int a = 3;
static {
a = 100;
System.out.println(a);
}
public SimpleClass() {
System.out.println("對類進行加載!");
}
public void run() {
System.out.println("我要泡泡竣蹦!");
}
}
步驟一:裝載ClassLoaderProduce類顶猜,在方法區(qū)生成動態(tài)數(shù)據(jù)結構(靜態(tài)變量、靜態(tài)方法痘括、常量池长窄、類代碼)滔吠,并且在堆中生成java.lang.class對象;讓后鏈接
步驟二:初始化--把static{}與靜態(tài)變量合并放在類構造器<clinit>()當中挠日,對靜態(tài)變量賦值疮绷。1-5執(zhí)行完畢。
步驟三:執(zhí)行main方法嚣潜,首先在棧里面生成一個main方法的棧幀冬骚,定義變量b、c懂算,注意此處的變量b只冻、c存儲的常量池存儲的變量的地址,如圖所示计技。
步驟四:創(chuàng)建SimpleClass對象喜德;與上面的步驟一類型:加載-鏈接-初始化。然后垮媒,調用run()方法的時候住诸,它會通過classLoader局部變量的地址尋找到類的class對象并且調用run()方法。