字節(jié)碼
xx.java -> 編譯器 -> cc.class -> JVM
可以通過xxd xx.class命令查看字節(jié)碼文件
cafe babe 是JVM識別.class文件的標志混卵,被稱為“魔數(shù)”秧均。
類加載過程
類加載的5個階段:載入跋破、驗證辈末、準備、解析孝偎、初始化
一般這5個階段是順序發(fā)生的访敌,但是在動態(tài)綁定的情況下,解析階段發(fā)生在初始化階段之后衣盾。
1. Loading 載入
JVM 將字節(jié)碼從不同的數(shù)據(jù)源(可能是 class 文件寺旺、也可能是 jar 包,甚至網(wǎng)絡)轉化為二進制字節(jié)流加載到內存中雨效,并生成一個代表該類的 java.lang.Class 對象迅涮。
2. Verification 驗證
JVM 會在該階段對二進制字節(jié)流進行校驗,只有符合 JVM 字節(jié)碼規(guī)范的才能被 JVM 正確執(zhí)行徽龟。
驗證階段大致會完成4個階段的檢驗動作:
- 文件格式驗證:驗證字節(jié)流是否符合Class文件格式的規(guī)范叮姑;例如:是否以0xCAFEBABE開頭、主次版本號是否在當前虛擬機的處理范圍之內据悔、常量池中的常量是否有不被支持的類型传透。
- 元數(shù)據(jù)驗證:對字節(jié)碼描述的信息進行語義分析(注意:對比javac編譯階段的語義分析),以保證其描述的信息符合Java語言規(guī)范的要求极颓;例如:這個類是否有父類朱盐,除了java.lang.Object之外。
- 字節(jié)碼驗證:通過數(shù)據(jù)流和控制流分析菠隆,確定程序語義是合法的兵琳、符合邏輯的。
- 符號引用驗證:確保解析動作能正確執(zhí)行骇径。
驗證階段是非常重要的躯肌,但不是必須的,它對程序運行期沒有影響破衔,如果所引用的類經(jīng)過反復驗證清女,那么可以考慮采用-Xverifynone參數(shù)來關閉大部分的類驗證措施,以縮短虛擬機類加載的時間晰筛。
3. Preparation 準備
JVM 會在該階段為類的靜態(tài)變量分配內存嫡丙,并將其初始化為默認值(對應數(shù)據(jù)類型的默認初始值拴袭,如 0、0L曙博、null拥刻、false 等)。
也就是說羊瘩,假如有這樣一段代碼:
public String chenmo = "沉默";
public static String wanger = "王二";
public static final String cmower = "沉默王二";
chenmo 不會被分配內存泰佳,而 wanger 會盼砍;但 wanger 的初始值不是“王二”而是 null尘吗。把wanger賦值為“王二”的putstatic指令是在程序編譯后,存放于類構造器<clinit>()方法之中的浇坐,所以把賦值為“王二”的動作將在初始化階段才會執(zhí)行睬捶。
需要注意的是,static final 修飾的變量被稱作為常量近刘,和類變量不同擒贸。
常量一旦賦值就不會改變了,所以 cmower 在準備階段的值為“沉默王二”而不是 null觉渴。
4. Resolution 解析
該階段將常量池中的符號引用轉化為直接引用介劫。
符號引用以一組符號(任何形式的字面量,只要在使用時能夠無歧義的定位到目標即可)來描述所引用的目標案淋。
在編譯時座韵,Java 類并不知道所引用的類的實際地址,因此只能使用符號引用來代替踢京。比如 com.Wanger 類引用了 com.Chenmo 類誉碴,編譯時 Wanger 類并不知道 Chenmo 類的實際內存地址,因此只能使用符號 com.Chenmo瓣距。
直接引用通過對符號引用進行解析黔帕,找到引用的實際內存地址。
5. Initialization 初始化
該階段是類加載過程的最后一步蹈丸。在準備階段成黄,類變量已經(jīng)被賦過默認初始值,而在初始化階段逻杖,類變量將被賦值為代碼期望賦的值奋岁。換句話說,初始化階段是執(zhí)行類構造器方法的過程弧腥。
在Java中對類變量進行初始值設定有兩種方式:
①聲明類變量是指定初始值厦取。
②使用靜態(tài)代碼塊為類變量指定初始值。
類加載器
對于任意一個類管搪,都需要由它的類加載器和這個類本身一同確定其在 JVM 中的唯一性虾攻。也就是說铡买,如果兩個類的加載器不同,即使兩個類來源于同一個字節(jié)碼文件霎箍,那這兩個類就必定不相等(比如兩個類的 Class 對象不 equals)奇钞。
Java 類加載器可以分為三種。
1)啟動類加載器(Bootstrap Class-Loader)漂坏,加載 jre/lib 包下面的 jar 文件景埃,比如說常見的 rt.jar。
2)擴展類加載器(Extension or Ext Class-Loader)顶别,加載 jre/lib/ext 包下面的 jar 文件谷徙。
3)應用類加載器(Application or App Clas-Loader),根據(jù)程序的類路徑(classpath)來加載 Java 類驯绎。
public class Test {
public static void main(String[] args) {
ClassLoader loader = Test.class.getClassLoader();
while (loader != null) {
System.out.println(loader.toString());
loader = loader.getParent();
}
}
}
雙親委派模型
自定義類加載器 -> 應用程序類加載器 -> 擴展類加載器 -> 啟動類加載器
這種層次關系被稱作為雙親委派模型:如果一個類加載器收到了加載類的請求完慧,它會先把請求委托給上層加載器去完成,上層加載器又會委托上上層加載器剩失,一直到最頂層的類加載器屈尼;如果上層加載器無法完成類的加載工作時,當前類加載器才會嘗試自己去加載這個類拴孤。
使用雙親委派模型有一個很明顯的好處脾歧,那就是 Java 類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關系,這對于保證 Java 程序的穩(wěn)定運作很重要演熟。
https://zhuanlan.zhihu.com/p/73078336
https://zhuanlan.zhihu.com/p/25228545