虛擬機(jī)類(lèi)加載機(jī)制

PS:雖然最近更新頻率低了,但是思危一直沒(méi)有停止,共勉!
概述
類(lèi)加載的時(shí)機(jī)
類(lèi)加載的過(guò)程
類(lèi)加載器
類(lèi)加載器分類(lèi)
雙親委托模型
概述
類(lèi)加載的時(shí)機(jī)

遇到 new、getstatic/putstatic、invokestatic 指令時(shí),如果該類(lèi)沒(méi)有被初始化,則需對(duì)類(lèi)進(jìn)行初始化,上面指令分別對(duì)應(yīng)使用 new 關(guān)鍵字進(jìn)行對(duì)象實(shí)例化、讀取或設(shè)置一個(gè)靜態(tài)屬性、調(diào)用靜態(tài)方法,具體可以使用
javap命令查看字節(jié)碼文件的實(shí)現(xiàn)來(lái)驗(yàn)證;使用 java,lang.reflect 對(duì)類(lèi)進(jìn)行反射調(diào)用的時(shí)候,如果該類(lèi)沒(méi)有被初始化,則需對(duì)類(lèi)進(jìn)行初始化;
當(dāng)初始化一個(gè)類(lèi)的時(shí)候,如果其父類(lèi)還沒(méi)有進(jìn)行初始化,則先進(jìn)行該類(lèi)父類(lèi)的初始化;
當(dāng) JVM 啟動(dòng)時(shí),用戶(hù)指定要啟動(dòng)的主類(lèi),比如還有 main 方法的類(lèi),JVM 會(huì)先初始化這個(gè)類(lèi);
當(dāng)使用 JDK 1.7 的動(dòng)態(tài)語(yǔ)言支持時(shí),如果 java.lang.invoke.MethodHandler 實(shí)例最后解析結(jié)果是 REF_getStatic、REF_putStatic、REF_invokeStatic,如果這些句柄對(duì)應(yīng)的類(lèi)沒(méi)有進(jìn)行初始化,則需先對(duì)其進(jìn)行初始化,至于 MethodHandler ?可以理解為反射的另一種形式。
類(lèi)加載的過(guò)程
加載
從 zip 包中獲取,最終成為 jar、war 格式的基礎(chǔ)。
從網(wǎng)絡(luò)中獲取,典型應(yīng)用就是 Applet。
運(yùn)行時(shí)生成,典型應(yīng)用就是動(dòng)態(tài)代理技術(shù),在 java.lang.reflect.Proxy 中就是使用 ProxyGenerator.generatrProxyClass 來(lái)為特定接口生成形如 Proxy 的代理類(lèi)的二進(jìn)制字節(jié)流。
其他文件生成、數(shù)據(jù)庫(kù)中獲取等。
鏈接
驗(yàn)證:確保 class 文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,且不會(huì)危害虛擬機(jī)自身的安全,從整體來(lái)看,驗(yàn)證階段主要包括文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證和符號(hào)引用驗(yàn)證,具體驗(yàn)證內(nèi)容可以自行查看 Java 虛擬機(jī)規(guī)范。
準(zhǔn)備:正式為類(lèi)變量分配內(nèi)存并設(shè)置類(lèi)變量的初始值,這個(gè)初始值一般是數(shù)據(jù)類(lèi)型的初始值,而不是真正代碼中初始化的值,如 int 初始值就是 0,這些類(lèi)變量使用的內(nèi)存都將在方法區(qū)進(jìn)行分配,類(lèi)變量指的就是被 static 關(guān)鍵字修飾過(guò)的變量。
解析:JVM 將常量池中的符號(hào)引用替換為直接引用,這里的符號(hào)引用就是在前面驗(yàn)證階段提到的符號(hào)引用驗(yàn)證中的符號(hào)引用
初始化
初始化階段就是執(zhí)行類(lèi)構(gòu)造器
() 方法的過(guò)程。 <clinit>() 方法是由編譯器自動(dòng)收集類(lèi)中的所有類(lèi)變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊 statuc{} 中的語(yǔ)句合并產(chǎn)生的,編譯器收集順序和源碼中語(yǔ)句順序一致,如靜態(tài)語(yǔ)句塊中只能訪(fǎng)問(wèn)定義在它之前的變量,定義在它后面的變量只能復(fù)制不能訪(fǎng)問(wèn)。 初始化一個(gè)類(lèi)時(shí),如果父類(lèi)還沒(méi)初始化,則先進(jìn)行父類(lèi)的初始化。
JVM 會(huì)保證一個(gè)類(lèi)的
() 方法在多線(xiàn)程環(huán)境中被正確的加鎖、同步。 當(dāng)訪(fǎng)問(wèn)一個(gè) Java 類(lèi)的靜態(tài)域時(shí),只有真正聲明這個(gè)類(lèi)才會(huì)被初始化。
類(lèi)加載器
java.lang.ClassLoader 類(lèi)的一個(gè)實(shí)例,前面知道在類(lèi)的加載階段會(huì)通過(guò)類(lèi)加載器來(lái)加載 class 文件,也就是可以通過(guò)一個(gè)類(lèi)的全限定名來(lái)獲取定義此類(lèi)的二進(jìn)制字節(jié)流 ,這個(gè)動(dòng)作的代碼實(shí)現(xiàn)就是類(lèi)加載器的實(shí)現(xiàn)。類(lèi)加載器分類(lèi)
啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader):一般使用 C++ 語(yǔ)言實(shí)現(xiàn),具體由 JVM 實(shí)現(xiàn)。
其他類(lèi)加載器:使用 Java 語(yǔ)言實(shí)現(xiàn),獨(dú)立于 JVM 之外,且都是 java.lang.ClassLoader 的一個(gè)實(shí)例,如 Android 中的 DexClassLoader。
啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader):負(fù)責(zé)加載的是 JAVA_HOME\lib 下的類(lèi)庫(kù),或者被-Xbootclasspath 參數(shù)所指定的路徑中的并且是 JVM 識(shí)別的(僅按照文件名識(shí)別,如 rt.jar 等,名字不符合的類(lèi)庫(kù)即使放在 lib 目錄中也不會(huì)被加載),啟動(dòng)類(lèi)加載器無(wú)法被JAVA程序直接應(yīng)用。
擴(kuò)展類(lèi)加載器(Extension ClassLoader):這個(gè)類(lèi)加載器由
sum.misc.Launcher$ExtClassLoader實(shí)現(xiàn),負(fù)責(zé)加載 JAVA_HOME\lib\ext 下的類(lèi),或者是被 java.ext.dirs 系統(tǒng)變量所指定的路徑下的類(lèi)庫(kù),可以直接使用擴(kuò)展類(lèi)加載器。應(yīng)用程序類(lèi)加載器(Application ClassLoader):這個(gè)類(lèi)加載器由
sun.misc.Launcher$AppClassLoader實(shí)現(xiàn),這個(gè)類(lèi)加載器是 ClassLoader 中的getSystemClassLoader() 方法的返回值, 一般也稱(chēng)它為系統(tǒng)類(lèi)加載器,負(fù)責(zé)加載用戶(hù)類(lèi)路徑(ClassPath) 下所指定的類(lèi)庫(kù), 開(kāi)發(fā)者可以直接使用這個(gè)類(lèi)加載器, 如果應(yīng)用程序中沒(méi)有自定義過(guò)自己的類(lèi)加載器, 這個(gè)就是程序中默認(rèn)的類(lèi)加載器。
雙親委托模型

來(lái)實(shí)現(xiàn), 而是都使用組合(Composition) 關(guān)系來(lái)復(fù)用父加載器的代碼,這種方式并不是一個(gè)強(qiáng)制性的約束模型, 而是 Java 設(shè)計(jì)者推薦給開(kāi)發(fā)者的一種類(lèi)加載器實(shí)現(xiàn)方式。
1protected?Class>?loadClass(String?name,?boolean?resolve)throws?ClassNotFoundException{
2????synchronized?(getClassLoadingLock(name))?{
3????????//?1.?檢查是否已經(jīng)加載過(guò)類(lèi)
4????????Class>?c?=?findLoadedClass(name);
5????????if?(c?==?null)?{
6????????????long?t0?=?System.nanoTime();
7????????????try?{
8????????????????if?(parent?!=?null)?{
9????????????????????//?2.?如果沒(méi)有被加載,則調(diào)用父類(lèi)的類(lèi)加載器加載
10????????????????????c?=?parent.loadClass(name,?false);
11????????????????}?else?{
12????????????????????//?3.?如果父類(lèi)的類(lèi)加載器不存在,則直接使用啟動(dòng)類(lèi)加載器進(jìn)行加載
13????????????????????c?=?findBootstrapClassOrNull(name);
14????????????????}
15????????????}?catch?(ClassNotFoundException?e)?{
16????????????????//?ClassNotFoundException?thrown?if?class?not?found
17????????????????//?from?the?non-null?parent?class?loader
18????????????}
19
20????????????if?(c?==?null)?{
21????????????????//?If?still?not?found,?then?invoke?findClass?in?order
22????????????????//?to?find?the?class.
23????????????????long?t1?=?System.nanoTime();
24????????????????//?4.?父類(lèi)或啟動(dòng)類(lèi)加載器都沒(méi)有加載該類(lèi),則調(diào)用自己的也就是子類(lèi)類(lèi)加載器的findClass犯法進(jìn)行類(lèi)加載
25????????????????c?=?findClass(name);
26????????????????sun.misc.PerfCounter.getParentDelegationTime().addTime(t1?-?t0);
27????????????????sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
28????????????????sun.misc.PerfCounter.getFindClasses().increment();
29????????????}
30????????}
31????????if?(resolve)?{
32????????????resolveClass(c);
33????????}
34????????return?c;
35????}
36}
