JVM 類加載器分類1. 前言我們之前對類加載子系統(tǒng)進(jìn)行過簡要的介紹,此處我們將會進(jìn)行更加細(xì)致的講解。本節(jié)主要知識點如下: 啟動(Bootstrap)類加載器的作用及代碼驗證,為本節(jié)重點內(nèi)容之一; 擴(kuò)展(Extension)類加載器的作用及代碼驗證,為本節(jié)重點內(nèi)容之一; 系統(tǒng)(System Application)類加載器的作用及代碼驗證,為本節(jié)重點內(nèi)容之一。
通篇皆為重點內(nèi)容,都是學(xué)習(xí)者需要重點掌握的。并且此節(jié)的內(nèi)容也是后續(xù)內(nèi)容的知識基礎(chǔ),為了更順利的進(jìn)行學(xué)習(xí),次節(jié)內(nèi)容需要重點掌握。 2. 類加載子系統(tǒng)知識回顧我們在JVM 總體架構(gòu)的講解過程中,提到過類加載子系統(tǒng)的工作流程分為三步:加載->鏈接->初始化。如下圖所示: 本節(jié)我們所討論的內(nèi)容都是圍繞第一步“加載(Loading)” 進(jìn)行的。對于鏈接和初始化,我們會在后邊的章節(jié)進(jìn)行講解。 我們將加載(Loading)這一步,再進(jìn)行下細(xì)致的模塊劃分,如下圖所示: 從上圖中我們可看到,加載(loading)這一步,里邊包含了三個更加細(xì)粒度的模塊,分別為 BootStrap Class Loader,Extention Class Loader 和 Application Class Loader,這三個 Class Loader 就是我們加載過程中必須要使用到的三大類加載器。 3. 啟動(Bootstrap)類加載器定義:啟動(Bootstrap)類加載器也稱為引導(dǎo)類加載器,該加載器是用本地代碼實現(xiàn)的類加載器,它所加載的類庫絕大多數(shù)都是出自 %JAVA_HOME%/lib 下面的核心類庫,當(dāng)然還有其他少部分所需類庫。 由于引導(dǎo)類加載器涉及到虛擬機(jī)本地實現(xiàn)細(xì)節(jié),開發(fā)者無法直接獲取到啟動類加載器的引用,所以不允許直接通過引用進(jìn)行操作。 Tips:從上述定義的描述中,我們可以看到一個特別需要關(guān)注的點:啟動類加載器加載的絕對大多數(shù)是
%JAVA_HOME%/lib
下邊的核心類庫。這句話完完全全的體現(xiàn)出了啟動(Bootstrap)類加載器存在的意義。對于其他少部分核心類的加載,我們在代碼驗證過程中來講解。接下來,讓我們通過示例代碼進(jìn)行下驗證。
示例:通過編寫一個 main 函數(shù),打印出通過啟動(Bootstrap)類加載器加載的所有的類庫信息,以證實啟動(Bootstrap)類加載器加載的是 %JAVA_HOME%/lib 下邊的核心類庫。 Tips:注意下 main 函數(shù)代碼的第二行代碼 URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); 這是通過 sun 公司提供的 Launcher 包獲取 Bootstrap 類加載器下 ClassPath 下的所有的 URL。
import java.net.URL;
public class LoaderDemo { public static void main(String[] args) { System.out.println("BootstrapClassLoader 的加載路徑: "); URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for(URL url : urls) System.out.println(url); } }
結(jié)果驗證:運(yùn)行 main 函數(shù)。 Tips:此處運(yùn)行結(jié)果所打印的類庫的絕對路徑為本人本機(jī)的安裝路徑,學(xué)習(xí)者應(yīng)按照自己真實的JDK安裝路徑以及版本對號入座,此處僅為示例。
BootstrapClassLoader 的加載路徑: file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/resources.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/rt.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/sunrsasign.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/jsse.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/jce.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/charsets.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/jfr.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/classes
結(jié)果解析:我們可以看到,運(yùn)行結(jié)果中的前 7
個類庫(不同的JDK版本會有差異,此處我們討論的是JDK 1.8版本),都是出自lib下的核心類庫。但是對于最后一條加載信息卻不是 lib
下的類庫。我們仔細(xì)看下最后這條信息的加載 file:/D:/Programs/Java/jdk1.8.0_111/jre/classes。 這就是前文我們所提到的其他少部分的核心類庫加載,學(xué)習(xí)者可以根據(jù)自己真實的安裝位置打開
/jre 文件夾,看看是否存在 /classes 路徑。結(jié)果是 /classes 文件夾路徑并不存在,除非我們進(jìn)行特殊的參數(shù)創(chuàng)建才可以出現(xiàn)
/classes 路徑。此處并非我們主要討論的問題,我們關(guān)注的是lib文件夾下的核心類庫加載,這里僅做了解即可。 4. 擴(kuò)展(Extension)類加載器定義:擴(kuò)展類加載器是由
Sun 公司提供的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)實現(xiàn)的,它負(fù)責(zé)將
%JAVA_HOME%/lib/ext 或者少數(shù)由系統(tǒng)變量 -Djava.ext.dir
指定位置中的類庫加載到內(nèi)存中。開發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類加載器。 Tips:此處我們依舊對大多數(shù)的核心類庫加載位置進(jìn)行討論,即 %JAVA_HOME%/lib/ext 文件夾下的擴(kuò)展核心類庫。對于系統(tǒng)變量指定的類庫,稍作了解即可。下邊進(jìn)行示例代碼驗證
示例: import java.net.URL; import java.net.URLClassLoader; public class LoaderDemo { public static void main(String[] args) { //取得擴(kuò)展類加載器 URLClassLoader extClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader().getParent(); System.out.println(extClassLoader); System.out.println("擴(kuò)展類加載器 的加載路徑: "); URL[] urls = extClassLoader.getURLs(); for(URL url : urls) System.out.println(url); } }
結(jié)果驗證:運(yùn)行 main 函數(shù)。 擴(kuò)展類加載器 的加載路徑: file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/access-bridge-64.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/cldrdata.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/dnsns.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/jaccess.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/jfxrt.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/localedata.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/nashorn.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunec.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunjce_provider.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunmscapi.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunpkcs11.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/zipfs.jar
結(jié)果解析:我們可以看到,運(yùn)行結(jié)果中所有的核心類庫均來自 %JAVA_HOME%/lib/ext 的文件夾。 5. 系統(tǒng)(System Application)類加載器定義:系統(tǒng)類加載器是由
Sun 公司提供的 AppClassLoader(sun.misc.Launcher$AppClassLoader)實現(xiàn)的,它負(fù)責(zé)將
用戶類路徑(java
-classpath或-Djava.class.path變量所指的目錄,即當(dāng)前類所在路徑及其引用的第三方類庫的路徑)下的類庫加載到內(nèi)存中。開發(fā)者可以直接使用系統(tǒng)類加載器。 Tips:系統(tǒng)(System Application)類加載器加載的核心類庫類型比較多,也會加載 lib
下的未被 BootStrap 類加載器加載的類庫,還會加載 ext 文件夾下的未被 Extension
類加載器加載的類庫,以及其他類庫??偠灾痪湓?,加載除了 BootStrap 類加載器和 Extension
類加載器所加載的其余的所有的核心類庫。
示例: import java.net.URL; import java.net.URLClassLoader; public class LoaderDemo { public static void main(String[] args) { //取得應(yīng)用(系統(tǒng))類加載器 URLClassLoader appClassLoader = (URLClassLoader)ClassLoader.getSystemClassLoader(); System.out.println(appClassLoader); System.out.println("應(yīng)用(系統(tǒng))類加載器 的加載路徑: "); URL[] urls = appClassLoader.getURLs(); for(URL url : urls) System.out.println(url); } }
結(jié)果驗證:運(yùn)行 main 函數(shù)。 應(yīng)用(系統(tǒng))類加載器 的加載路徑: file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/charsets.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/deploy.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/access-bridge-64.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/cldrdata.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/dnsns.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/jaccess.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/jfxrt.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/localedata.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/nashorn.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunec.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunjce_provider.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunmscapi.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/sunpkcs11.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/ext/zipfs.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/javaws.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/jce.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/jfr.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/jfxswt.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/jsse.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/management-agent.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/plugin.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/resources.jar file:/D:/Programs/Java/jdk1.8.0_111/jre/lib/rt.jar file:/E:/IdeaWorkspace/LeeCode/target/classes/ file:/D:/Programs/IntelliJ%20IDEA%20Educational%20Edition%202019.3.1/lib/idea_rt.jar
結(jié)果解析:我們可以看到, 系統(tǒng)(System Application)類加載器加載的類庫種類很多,除了之前兩種類加載器加載的類庫,其余必須的核心類庫,都由系統(tǒng)類加載器加載。 6. 小結(jié)對于類加載器中的第一步加載(Loading),我們主要講解了 3 種類加載器。并且對不同的類加載器所加載的類庫進(jìn)行了講解以及代碼驗證。通篇皆為重點知識,需要學(xué)習(xí)者用心學(xué)習(xí)。 對于加載(Loading)這一步,我們還未講解完,下節(jié)課程會講解加載(Loading)這一步所遵循的雙親委派模型,本節(jié)作為下一節(jié)的知識基礎(chǔ),更需要著重理解、掌握。 JVM 雙親委派模型1. 前言上節(jié)課程的小結(jié)部分提到,雙親委派模型是加載(Loading)步驟中所使用的模型,上節(jié)內(nèi)容為本節(jié)內(nèi)容的基礎(chǔ)知識,學(xué)習(xí)本節(jié)內(nèi)容之前,要確保已經(jīng)掌握了上節(jié)所講解的內(nèi)容。本節(jié)主要知識點如下: 了解雙親委派模型的示意圖,是本節(jié)課程的基礎(chǔ)知識; 掌握雙親委派模型的工作原理,并結(jié)合示意圖進(jìn)行理解,為本節(jié)內(nèi)容的核心知識點; 舉出三個不同類型的案例供學(xué)習(xí)者參考,更深入的了解雙親委派模型的工作原理,為本節(jié)內(nèi)容的重點內(nèi)容;
2. 雙親委派模型在講解雙親委派模型之前,我們先來看看雙親委派模型的示意圖,相信看到如下示意圖中的模塊信息,學(xué)習(xí)者會感到莫名的親切感。示意圖如下: 上圖中的親切感來自哪里呢?我們可以看到在雙親委派模型中,有三種類加載器是我們上節(jié)課程中所講解的,有了上節(jié)課程的知識積淀,再理解雙親委派模型會非常的容易。 雙親委派模型原理: 如果對于原理性的描述還是比較模糊的話,不要著急,我們繼續(xù)學(xué)習(xí)下邊的內(nèi)容,通過案例體會雙親委派模型的原理。 3. 案例 1:加載 /jre/lib/resources.jar通過上節(jié)課程的學(xué)習(xí),我們能夠知道 /jre/lib/resources.jar 是需要被啟動(BootStrap)類加載器加載的核心類庫,那么我們來看看它的加載流程圖。 加載流程圖:根據(jù)雙親委派模型,我們來看下 resources.jar 的完整加載過程。 從上圖中我們可以看到,對于核心類庫 resources.jar 的加載,分為以下 4 步: 步驟 1:resources.jar 會先通過自定義類加載器(前提是我們實現(xiàn)了自定義類加載器),自定義類加載器不會做處理,直接向上委托給系統(tǒng)(System Application)類加載器; 步驟 2:系統(tǒng)(System Application)類加載器接到委托后,也不做任何處理,直接向上委托給擴(kuò)展(Extension)類加載器; 步驟 3:擴(kuò)展(Extension)類加載器接到委托后,也不做任何處理,直接向上委托給啟動(Bootstrap)類加載器; 步驟 4:啟動(Bootstrap)類加載器接到委托后,發(fā)現(xiàn) resources.jar 是自己負(fù)責(zé)加載的核心類庫,于是進(jìn)行加載,最后成功加載了 resources.jar。
4. 案例 2:加載 /jre/lib/ext/cldrdata.jar通過上節(jié)課程的學(xué)習(xí),我們能夠知道 /jre/lib/ext/cldrdata.jar 是需要被擴(kuò)展(Extension)類加載器加載的核心類庫,那么我們來看看它的加載流程圖。 加載流程圖:根據(jù)雙親委派模型,我們來看下 cldrdata.jar 的完整加載過程。 從上圖中我們可以看到,對于核心類庫 cldrdata.jar 的加載,分為以下 5 步: 步驟 1:cldrdata.jar 會先通過自定義類加載器(前提是我們實現(xiàn)了自定義類加載器),自定義類加載器不會做處理,直接向上委托給系統(tǒng)(System Application)類加載器; 步驟 2:系統(tǒng)(System Application)類加載器接到委托后,也不做任何處理,直接向上委托給擴(kuò)展(Extension)類加載器; 步驟 3:擴(kuò)展(Extension)類加載器接到委托后,也不做任何處理,直接向上委托給啟動(Bootstrap)類加載器; 步驟 4:啟動(Bootstrap)類加載器接到委托后,發(fā)現(xiàn) cldrdata.jar 不是自己負(fù)責(zé)加載的核心類庫,于是進(jìn)行向下委派,委派給擴(kuò)展(Extension)類加載器; 步驟 5:擴(kuò)展(Extension)類加載器接到委派后,發(fā)現(xiàn) cldrdata.jar 是自己負(fù)責(zé)加載的核心類庫,于是進(jìn)行加載,最后成功加載了 cldrdata.jar。
Tips:我們可以看到,在向上委托的過程中,cldrdata.jar 雖然在步驟 3
已經(jīng)達(dá)到了擴(kuò)展(Extension)類加載器,但是由于擴(kuò)展(Extension)類加載器需要遵循向上委托的原則,必須要將
cldrdata.jar 向上委托給啟動(Bootstrap)類加載器,直到啟動(Bootstrap)類加載器向下委派 cldrdata.jar
到擴(kuò)展(Extension)類加載器才進(jìn)行類庫的加載。
5. 案例 3:加載 /jre/lib/plugin.jar通過上節(jié)課程的學(xué)習(xí),我們能夠知道 /jre/lib/plugin.jar 是需要被系統(tǒng)(System Application)類加載器加載的核心類庫,那么我們來看看它的加載流程圖。 加載流程圖:根據(jù)雙親委派模型,我們來看下 plugin.jar 的完整加載過程。 從上圖中我們可以看到,對于核心類庫 plugin.jar 的加載,分為以下 6 步: 步驟 1:plugin.jar 會先通過自定義類加載器(前提是我們實現(xiàn)了自定義類加載器),自定義類加載器不會做處理,直接向上委托給系統(tǒng)(System Application)類加載器; 步驟 2:系統(tǒng)(System Application)類加載器接到委托后,也不做任何處理,直接向上委托給擴(kuò)展(Extension)類加載器; 步驟 3:擴(kuò)展(Extension)類加載器接到委托后,也不做任何處理,直接向上委托給啟動(Bootstrap)類加載器; 步驟 4:啟動(Bootstrap)類加載器接到委托后,發(fā)現(xiàn) plugin.jar 不是自己負(fù)責(zé)加載的核心類庫,于是進(jìn)行向下委派,委派給擴(kuò)展(Extension)類加載器; 步驟 5:擴(kuò)展(Extension)類加載器接到委派后,發(fā)現(xiàn) plugin.jar 也不是自己負(fù)責(zé)加載的核心類庫,于是進(jìn)行向下委派,委派給系統(tǒng)(System Application)類加載器; 步驟 6:系統(tǒng)(System Application)類加載器接到委派后,發(fā)現(xiàn) plugin.jar 是自己負(fù)責(zé)加載的核心類庫,于是進(jìn)行加載,最后成功加載了 plugin.jar。
Tips:類似于案例 2 的講解,雖然 plugin.jar 是系統(tǒng)(System Application)類加載器負(fù)責(zé)加載的,但是要遵循向上委托的原則,因此在步驟 2 不能夠?qū)崟r加載,只能等待父加載器向下委派時加載。
6. 小結(jié)通過對雙親委派模型的講解,我們了解到了雙親委派模型的定義以及原理,并通過 3 個案例詳細(xì)的講述了在雙親委派模型下是如何進(jìn)行的類庫的加載。 通篇皆為重點內(nèi)容,所有的內(nèi)容都是圍繞雙親委派模型的原理展開的,需要學(xué)習(xí)者仔細(xì)品味案例,掌握雙親委派模型的原理。 JVM 中類加載的鏈接與初始化1. 前言對于類加載子系統(tǒng),前邊的課程已經(jīng)對加載(Loading)這一步做了詳細(xì)的講解,本節(jié)主要對類加載子系統(tǒng)加載步驟中的鏈接與初始化進(jìn)行講解。本節(jié)主要知識點如下: 鏈接(Linking)步驟更加詳細(xì)的模塊劃分:驗證,準(zhǔn)備和解析,為本節(jié)基礎(chǔ)知識點; 掌握在鏈接(Linking)步驟中的第一步驗證的詳細(xì)驗證內(nèi)容,為本節(jié)重點內(nèi)容之一; 掌握在鏈接(Linking)步驟中的第二步準(zhǔn)備的準(zhǔn)備內(nèi)容,為本節(jié)重點內(nèi)容之一; 掌握在鏈接(Linking)步驟中的第三步解析的具體解析內(nèi)容,為本節(jié)重點內(nèi)容之一; 掌握初始化(Init)步驟中的規(guī)則以及實例初始化順序,為本節(jié)重點內(nèi)容之一。
通篇皆為重點內(nèi)容,本節(jié)知識也會為類加載子系統(tǒng)部分畫上一個完美的句號,一定要認(rèn)真對待。 2. 類加載子系統(tǒng)知識回顧我們在JVM 總體架構(gòu)的講解過程中,提到過類加載子系統(tǒng)的工作流程分為三步:加載->鏈接->初始化。如下圖所示: 本節(jié)我們所討論的內(nèi)容都是圍繞第二步“鏈接(Linking)” 和第三步“初始化(Init)”進(jìn)行的。 我們將鏈接(Linking)這一步,再進(jìn)行下細(xì)致的模塊劃分,如下圖所示: 從上圖中我們可看到,鏈接(Linking)這一步,里邊包含了三個更加細(xì)致的步驟,分別為驗證(verify),準(zhǔn)備(prepare)和解析(resolve)。后文我們會對這三個步驟進(jìn)行講解。 3. 鏈接-驗證(verify)定義:驗證是連接階段的第一步,這一階段的目的是為了確保 Class 文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并不會危害虛擬機(jī)的自身安全。 驗證過程的主要驗證信息:驗證過程中,主要對三種類型的數(shù)據(jù)進(jìn)行驗證,分別是“元數(shù)據(jù)驗證,字節(jié)碼驗證和符號引用驗證”。具體內(nèi)容請看下邊的講解。 元數(shù)據(jù)驗證: 驗證這個類是否有父類(除了 java.lang.Object 之外,所有類都應(yīng)當(dāng)有父類); 驗證這個類是否繼承了不允許被繼承的類(被 final 修飾的類); 如果這個類不是抽象類,驗證該類是否實現(xiàn)了其父類或接口之中所要求實現(xiàn)的所有方法; 驗證類中的字段、方法是否與父類產(chǎn)生矛盾(例如覆蓋了父類的 final 字段,或者出現(xiàn)不符合規(guī)則的方法重載,例如方法參數(shù)都一致,但返回值類型卻不同等等)。
字節(jié)碼驗證:字節(jié)碼驗證主要目的是通過數(shù)據(jù)流和控制流分析,確定程序語義是合法的、符合邏輯的。這個階段將對類的方法體進(jìn)行校驗分析,保證被校驗類的方法在運(yùn)行時不會產(chǎn)生危害虛擬機(jī)安全的事件,例如: 保證任意時刻操作數(shù)棧的數(shù)據(jù)類型與指令代碼序列都能配合工作。例如不會出現(xiàn)類似這樣的情況:在操作數(shù)棧放置了一個int類型的數(shù)據(jù),使用時卻按long類型來加載入本地變量表中; 保證跳轉(zhuǎn)指令不會跳轉(zhuǎn)到方法體以外的字節(jié)碼指令上; 保證方法體中的類型轉(zhuǎn)換是有效的,例如可以把一個子類對象賦值給父類數(shù)據(jù)類型,但是把父類對象賦值給子類數(shù)據(jù)類型,甚至把對象賦值給與它毫無繼承關(guān)系、完全不相干的一個數(shù)據(jù)類型,則是危險不合法的。
符號引用驗證:符號引用驗證可以看作是類對自身以外(常量池中的各種符號引用)的信息進(jìn)行匹配性校驗,通常需要校驗以下內(nèi)容: 符號引用中通過字符串描述的全限定名是否能夠找到對應(yīng)的類; 在指定類中是否存在符合方法的字段描述符以及簡單名稱所描述的方法和字段; 符號引用中的類、字段、方法的訪問性(private、default、protected、public)是否可被當(dāng)前類訪問。
4. 鏈接-準(zhǔn)備(prepare)定義:準(zhǔn)備階段是正式為類變量分配內(nèi)存并設(shè)置類變量默認(rèn)值(通常情況下是數(shù)據(jù)類型的零值)的階段,這些變量所使用的內(nèi)存都將在方法區(qū)中進(jìn)行分配。這時候進(jìn)行內(nèi)存分配的僅包括類變量(被static修飾的變量),而不包括實例變量,實例變量將會在對象實例化的時候隨著對象一起分配在Java堆中。 Tips:準(zhǔn)備階段是設(shè)置類變量的默認(rèn)值,不同類型的類變量的默認(rèn)值是不同的。變量默認(rèn)值的對照表請參看下表:
變量類型 默認(rèn)值 int 0 long 0L short 0 char '\u0000’ byte 0 boolean false folat 0.0f double 0.0d reference null 5. 鏈接-解析(resolve)定義:解析階段是虛擬機(jī)將常量池內(nèi)的符號引用替換為直接引用的過程。 Tips:定義中又引出了兩個新的概念:符號引用和直接引用。想要理解解析,必須要先搞明白什么是符號引用和直接引用。
符號引用(Symbolic References):符號引用以一組符號來描述所引用的目標(biāo),符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標(biāo)即可。 直接引用(Direct References):直接引用可以是直接指向目標(biāo)的指針、相對偏移量或是一個能間接定位到目標(biāo)的句柄。如果有了直接引用,那么引用的目標(biāo)一定是已經(jīng)存在于內(nèi)存中。 解析過程具體的解析內(nèi)容:解析過程中,主要對如下4種類型的數(shù)據(jù)進(jìn)行驗證: 類或接口的解析; 字段解析; 類方法解析; 接口方法解析。
6. 初始化定義:進(jìn)行準(zhǔn)備階段時,變量已經(jīng)賦過一次系統(tǒng)要求的初始零值,而在初始化階段,則會根據(jù)程序員通過程序編碼制定的主觀計劃去初始化類變量和其他資源。 類的初始化階段是類加載過程的最后一個步驟,之前介紹的幾個類加載的動作里,除了在加載階段用戶應(yīng)用程序可以通過自定義類加載器的方式局部參與外,其余動作都完全由Java虛擬機(jī)來主導(dǎo)控制。直到初始化階段,Java虛擬機(jī)才真正開始執(zhí)行類中編寫的
Java 程序代碼,將主導(dǎo)權(quán)移交給應(yīng)用程序。 實例的初始化順序:在進(jìn)行初始化時,實例變量的初始化順序如下圖所示: 實例的初始化順序是非常重要的知識點,在面試過程中也經(jīng)常涉及到這個知識點,上圖的加載順序需要重點掌握。 7. 小結(jié)到目前為止,類加載器子系統(tǒng)就全部講解完成了。我們學(xué)習(xí)了類的加載,三種類加載器,雙親委派模型以及本節(jié)所講述的鏈接與初始化,其中對鏈接有細(xì)分了三個步驟進(jìn)行了講解。 類加載器子系統(tǒng)是非常重要的 JVM 模塊,需要用心學(xué)習(xí),對于一些概念性知識要增強(qiáng)理解,原理性知識要深入思索。后續(xù)我們會繼續(xù)講解 JVM 的其他重要模塊。
|