虛擬機(jī)類加載機(jī)制

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

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

來實(shí)現(xiàn), 而是都使用組合(Composition) 關(guān)系來復(fù)用父加載器的代碼,這種方式并不是一個(gè)強(qiáng)制性的約束模型, 而是 Java 設(shè)計(jì)者推薦給開發(fā)者的一種類加載器實(shí)現(xiàn)方式。
1protected?Class>?loadClass(String?name,?boolean?resolve)throws?ClassNotFoundException{
2????synchronized?(getClassLoadingLock(name))?{
3????????//?1.?檢查是否已經(jīng)加載過類
4????????Class>?c?=?findLoadedClass(name);
5????????if?(c?==?null)?{
6????????????long?t0?=?System.nanoTime();
7????????????try?{
8????????????????if?(parent?!=?null)?{
9????????????????????//?2.?如果沒有被加載,則調(diào)用父類的類加載器加載
10????????????????????c?=?parent.loadClass(name,?false);
11????????????????}?else?{
12????????????????????//?3.?如果父類的類加載器不存在,則直接使用啟動(dòng)類加載器進(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.?父類或啟動(dòng)類加載器都沒有加載該類,則調(diào)用自己的也就是子類類加載器的findClass犯法進(jìn)行類加載
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}
