一蛮放、概述
? ? ? 虛擬機把描述類的數(shù)據(jù)從class文件加載到內(nèi)存拍皮,并對數(shù)據(jù)進行校驗撵割、轉(zhuǎn)換解析和初始化贿堰,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制睁枕。
二官边、類加載的時機
? ? ? 類從被加載到虛擬機內(nèi)存中開始,到卸載出內(nèi)存為止外遇,它的整個生命周期包括:加載(Loading)注簿、驗證(Verification)、準備(Preparation)跳仿、解析(Resolution)诡渴、初始化(Initialization)、使用(Using)和卸載(Unloading)7個階段。其中驗證妄辩、準備惑灵、解析3個部分統(tǒng)稱為連接(Linking),順序如下圖眼耀,圖7-1 類的生命周期
? ? ? ? 其中加載英支、驗證、準備哮伟、初始化和卸載這5個階段的順序是確定的干花,類加載過程必須按照這種順序開始。
? ? ? Java虛擬機規(guī)范中沒有對加載進行強制約束楞黄,由虛擬機的具體實現(xiàn)來自由把握池凄。對于初始化階段,則有嚴格規(guī)定了有且只有五中情況必須立即執(zhí)行初始化:
? ? ? ? 1鬼廓、遇到new肿仑、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時碎税,如果沒有進行初始化過尤慰,則需要先觸發(fā)其初始化。
? ? ? ? 2蚣录、使用java.lang.reflect包的方法對類進行反射調(diào)用的時候割择,如果類沒有進行過初始化,則需要先觸發(fā)其初始化萎河。
? ? ? ? 3荔泳、當初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒進行初始化虐杯,則需要先觸發(fā)其父類的初始化玛歌。
? ? ? ? 4、當虛擬機啟動時擎椰,用戶需要指定一個要執(zhí)行的主類(包含main方法的那個類)支子,虛擬機會先初始化這個主類。
? ? ? ? 5达舒、當時用JDK1.7的動態(tài)語言支持時值朋,如果一個java.lang.invoke.MethodHandle實例最后解析結(jié)果REF_getStatic、REF_putStatic巩搏、REF_invokeStatic的方法句柄昨登,并且句柄所對應(yīng)的類沒有進行初始化,則需要先觸發(fā)其初始化贯底。
? ? ? ? 以上五種場景中的行為稱為對一個類的主動引用丰辣,除此之外,所有引用類的方法都不會觸發(fā)初始化,被稱為被動引用笙什。
三飘哨、類加載的過程
? ? ? ? 1、加載
? ? ? ? 加載是類加載過程的一個階段琐凭。在加載階段需要完成以下3件事情:
? ? ? ? 1)芽隆、通過一個類的全限定名來獲取定義此類的二進制字節(jié)流。
? ? ? ? 2)统屈、將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu)摆马。
? ? ? ? 3)、在內(nèi)存中生成一個代表這個類的java.lang.Class對象鸿吆,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口。
? ? ? ? 2述呐、驗證
? ? ? ? 驗證是連接階段的第一步惩淳,這一階段的目的是為了確保Class文件的字節(jié)流中包含的信息符合當前虛擬機的要求,并且不會危害虛擬機自身的安全乓搬。驗證階段是非常重要的思犁,這個階段是否嚴謹,直接決定了Java虛擬機是否能承受惡意代碼的攻擊进肯,從執(zhí)行性能的角度上講激蹲,驗證階段的工作量在虛擬機的類加載子系統(tǒng)中又占了相當大的一部分。驗證大致會完成下面4個階段的檢驗動作:文件格式驗證江掩、元數(shù)據(jù)驗證学辱、字節(jié)碼驗證、符號運用驗證环形。
? ? ? ? ? 3策泣、準備
? ? ? ? ? 準備階段是正式為類變量分配內(nèi)存并設(shè)置類變量初始值的階段,這些變量都將在方法區(qū)中進行分配抬吟。首先萨咕,這時候進行內(nèi)存分配的僅包括類變量,而不包括實例變量火本,實例變量將會在對象實例化時隨著對象一起分配在Java堆中危队。其次,這里所說的初始化“通常情況”下是數(shù)據(jù)類型的零值钙畔,假設(shè)一個類變量的定義為:public static int value=123茫陆; 那么變量value在準備階段過后的初始值為0而不是123,將value賦值為123的動作將在初始化階段才會執(zhí)行刃鳄。? ?
? ? ? ? ? 4盅弛、解析
? ? ? ? ? 解析階段是虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程。
? ? ? ? ? 符號引用(Symbolic References):符號引用以一組符號來描述引用所引用的目標,符號可以是任何形式的字面量挪鹏,只要使用時能無歧義地定位到目標即可见秽。符號引用與虛擬機實現(xiàn)的內(nèi)存布局無關(guān),引用的目標并不一定已經(jīng)加載到內(nèi)存中讨盒。各種虛擬機實現(xiàn)的內(nèi)存布局可以不相同解取,但是它們能接受的符號引用必須是一致的,因為符號引用的字面量形式明確定義在Java虛擬機規(guī)范的Class文件格式中返顺。
? ? ? ? ? 直接引用:(Direct References):直接引用可以直接指向目標的指針禀苦、相對偏移量或是一個能間接定位到目標的句柄。直接引用是和虛擬機實現(xiàn)的內(nèi)存布局相關(guān)的遂鹊,同一個符號引用在不同虛擬機實例上翻譯出來的直接引用一般不會相同振乏。如果有了直接引用,那引用的目標必定在內(nèi)存中存在秉扑。
? ? ? ? 解析動作主要針對類或者接口慧邮、字段、類方法舟陆、接口方法误澳、方法類型、方法句柄和調(diào)用點限定符7類符號引用進行秦躯,分別對應(yīng)于常量池的CONSTANT_Class_info忆谓、CONSTANT_Fieldref_info、CONSTANT_Methodref_info踱承、CONSTANT_InterfaceMethodref_info倡缠、CONSTANT_MethodType_info、CONSTANT_MethodHandle_info和CONSTANT_InvokeDynamic_info 7種常量類型茎活。
? ? ? ? 5毡琉、初始化
? ? ? ? 類初始化階段是類加載過程的最后一步,這個階段才真正開始執(zhí)行類中定義的Java程序代碼妙色。
? ? ? ? 初始化階段是執(zhí)行類構(gòu)造器()方法的過程桅滋。
四、類加載器
? ? ? ? 虛擬機設(shè)計團隊把類加載階段中的“通過一個類的全限定名來獲取描述此類的二進制字節(jié)流”這個動作放到Java虛擬機外部去實現(xiàn)身辨,以便讓應(yīng)用程序自己決定如何去獲取所需要的類丐谋。實現(xiàn)這個動作的代碼模塊稱為“類加載器”。
? ? ? ? 1煌珊、雙親委派模型
? ? ? ? 從Java虛擬機的角度講号俐,只存在兩種不同的類加載器:一種是啟動類加載器(Bootstrap ClassLoader),是虛擬機的一部分定庵;另一種就是所有其他類加載器吏饿,獨立于虛擬機外部踪危,并且全部都繼承自抽象類java.lang.ClassLoader。
? ? ? 絕大部分Java程序都會使用到啟動類加載器(Bootstrap ClassLoader)猪落、擴展類加載器(Extension ClassLoader)贞远、應(yīng)用程序類加載器(Application ClassLoader)這三個系統(tǒng)提供的類加載器。
? ? ? 雙親委派模型要求除了頂層的啟動類加載器外笨忌,其余的類加載器都應(yīng)當有自己的父類加載器蓝仲。這里的類加載之間的父子關(guān)系一般不會以繼承(Inheritance)的關(guān)系來實現(xiàn),而都是組合(Composition)關(guān)系來復(fù)用父加載器的代碼官疲。圖7-2 類加載器雙親委派模型
? ? ? ? 雙親委派模型的工作過程是:如果一個類加載器收到了類加載的請求袱结,它首先不會自己嘗試加載這個類,而是把這個請求委派給父類加載器去完成途凫,每一個層次的類加載器都是如此垢夹,因此所有的加載請求最終都應(yīng)該傳送到頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求時维费,子加載器才會嘗試自己去加載棚饵。
? ? ? ? 2、破壞雙親委派模型
本文來自于《深入Java虛擬機-JVM高級特性與最佳實踐》---周志明掩完。如果侵權(quán),請聯(lián)系作者刪除硼砰。